lsp

package
v1.10.1 Latest Latest
Warning

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

Go to latest
Published: Mar 13, 2026 License: Apache-2.0 Imports: 16 Imported by: 0

Documentation

Overview

Package lsp implements a production-ready Language Server Protocol (LSP) server for GoSQLX.

The LSP server provides comprehensive SQL code intelligence features for IDEs and text editors, enabling real-time syntax validation, intelligent auto-completion, code formatting, and interactive documentation for SQL development.

Overview

The GoSQLX LSP server transforms any LSP-compatible editor into a powerful SQL development environment. It leverages the GoSQLX SQL parser to provide accurate, real-time feedback on SQL syntax and offers intelligent code assistance through the Language Server Protocol.

Version: 1.0.0 (GoSQLX v1.6.0+)

Features

The server implements the following LSP capabilities:

Diagnostics (textDocument/publishDiagnostics):

  • Real-time SQL syntax validation
  • Precise error location with line and column information
  • Structured error codes from GoSQLX parser
  • Immediate feedback as you type

Formatting (textDocument/formatting):

  • Intelligent SQL code formatting
  • Keyword capitalization
  • Consistent indentation (configurable tab/space)
  • Clause alignment for readability

Hover (textDocument/hover):

  • Interactive documentation for 60+ SQL keywords
  • Markdown-formatted help with syntax examples
  • Context-sensitive keyword information
  • Coverage: DML, DDL, JOINs, CTEs, Window Functions, Set Operations

Completion (textDocument/completion):

  • Auto-complete for 100+ SQL keywords
  • 22 pre-built code snippets for common patterns
  • Trigger characters: space, dot, opening parenthesis
  • Smart filtering based on current input

Document Symbol (textDocument/documentSymbol):

  • Outline view of SQL statements
  • Navigate between SELECT, INSERT, UPDATE, DELETE statements
  • Hierarchical structure for complex queries
  • Quick jump to specific statements

Signature Help (textDocument/signatureHelp):

  • Parameter hints for 20+ SQL functions
  • Active parameter highlighting
  • Documentation for each parameter
  • Coverage: Aggregates, Window Functions, String Functions, Type Conversions

Code Actions (textDocument/codeAction):

  • Quick fixes for common syntax errors
  • Automatic semicolon insertion
  • Keyword case correction suggestions
  • Context-aware refactoring hints

Architecture

The LSP server consists of three main components:

Server (server.go):

  • Main server loop and JSON-RPC 2.0 message handling
  • Rate limiting (100 requests/second) to prevent abuse
  • Message size limits (10MB per message, 5MB per document)
  • Graceful error handling and recovery
  • Thread-safe write operations

Handler (handler.go):

  • Implementation of all LSP protocol methods
  • Request routing and response generation
  • Integration with GoSQLX parser for validation
  • Error position extraction and diagnostic creation

DocumentManager (documents.go):

  • Thread-safe document state management
  • Support for incremental document synchronization
  • Version tracking for stale diagnostic detection
  • Efficient position-to-offset conversions

Protocol (protocol.go):

  • Complete LSP protocol type definitions
  • JSON-RPC 2.0 message structures
  • Standard and LSP-specific error codes
  • All LSP 3.17 data structures

Quick Start

Starting the LSP server from command line:

./gosqlx lsp
./gosqlx lsp --log /tmp/gosqlx-lsp.log  # With debug logging

Programmatic usage:

package main

import (
    "log"
    "os"
    "github.com/ajitpratap0/GoSQLX/pkg/lsp"
)

func main() {
    // Create logger that writes to file (not stdout!)
    logFile, err := os.Create("/tmp/gosqlx-lsp.log")
    if err != nil {
        log.Fatal(err)
    }
    defer logFile.Close()

    logger := log.New(logFile, "[GoSQLX LSP] ", log.LstdFlags)

    // Create and run server
    server := lsp.NewStdioServer(logger)
    if err := server.Run(); err != nil {
        logger.Fatalf("Server error: %v", err)
    }
}

IDE Integration

The LSP server integrates with popular editors and IDEs:

VSCode:

Add to your settings.json or create a VSCode extension:

{
  "gosqlx-lsp": {
    "command": "gosqlx",
    "args": ["lsp"],
    "filetypes": ["sql"],
    "settings": {}
  }
}

Or create .vscode/settings.json:

{
  "sql.lsp.path": "gosqlx",
  "sql.lsp.args": ["lsp"],
  "sql.lsp.logLevel": "info"
}

Neovim (nvim-lspconfig):

Add to your init.lua:

local lspconfig = require('lspconfig')
local configs = require('lspconfig.configs')

if not configs.gosqlx then
  configs.gosqlx = {
    default_config = {
      cmd = {'gosqlx', 'lsp'},
      filetypes = {'sql'},
      root_dir = lspconfig.util.root_pattern('.git', '.gosqlx.yml'),
      settings = {},
    },
  }
end

lspconfig.gosqlx.setup{}

Or using vim.lsp.start directly:

vim.api.nvim_create_autocmd("FileType", {
  pattern = "sql",
  callback = function()
    vim.lsp.start({
      name = "gosqlx-lsp",
      cmd = {"gosqlx", "lsp"},
      root_dir = vim.fn.getcwd(),
    })
  end,
})

Emacs (lsp-mode):

Add to your init.el:

(require 'lsp-mode)

(add-to-list 'lsp-language-id-configuration '(sql-mode . "sql"))

(lsp-register-client
 (make-lsp-client
  :new-connection (lsp-stdio-connection '("gosqlx" "lsp"))
  :activation-fn (lsp-activate-on "sql")
  :major-modes '(sql-mode)
  :server-id 'gosqlx-lsp))

(add-hook 'sql-mode-hook #'lsp)

Helix Editor:

Add to ~/.config/helix/languages.toml:

[[language]]
name = "sql"
language-server = { command = "gosqlx", args = ["lsp"] }

Sublime Text (LSP package):

Add to LSP.sublime-settings:

{
  "clients": {
    "gosqlx": {
      "enabled": true,
      "command": ["gosqlx", "lsp"],
      "selector": "source.sql"
    }
  }
}

Configuration

The LSP server can be configured via .gosqlx.yml in your project root:

# SQL dialect (postgresql, mysql, sqlite, sqlserver, oracle, generic)
dialect: postgresql

# Linting rules (see docs/LINTING_RULES.md)
linter:
  enabled: true
  rules:
    L001: error  # Keyword capitalization
    L002: warn   # Indentation style
    L003: error  # Trailing whitespace

# Formatting options
formatter:
  indent_size: 2
  indent_style: space
  keyword_case: upper
  max_line_length: 100

See docs/CONFIGURATION.md for complete configuration reference.

Keyword Documentation

The LSP server provides hover documentation for these SQL keyword categories:

Core DML (Data Manipulation):

SELECT, INSERT, UPDATE, DELETE, MERGE
FROM, WHERE, SET, VALUES

JOINs:

JOIN, INNER JOIN, LEFT JOIN, RIGHT JOIN, FULL OUTER JOIN
CROSS JOIN, NATURAL JOIN, LATERAL JOIN (PostgreSQL)
ON, USING

Filtering and Grouping:

WHERE, GROUP BY, HAVING, ORDER BY, LIMIT, OFFSET
DISTINCT, DISTINCT ON (PostgreSQL)
FETCH FIRST (SQL standard)

CTEs (Common Table Expressions):

WITH, RECURSIVE
Support for multiple CTEs and recursive queries

Set Operations:

UNION, UNION ALL, EXCEPT, INTERSECT
Proper precedence and parenthesization

Window Functions (SQL-99):

ROW_NUMBER, RANK, DENSE_RANK, NTILE
LAG, LEAD, FIRST_VALUE, LAST_VALUE
OVER, PARTITION BY, ORDER BY
ROWS BETWEEN, RANGE BETWEEN
UNBOUNDED PRECEDING, CURRENT ROW, UNBOUNDED FOLLOWING

Aggregate Functions:

COUNT, SUM, AVG, MIN, MAX
FILTER clause (SQL:2003)
ORDER BY in aggregates (PostgreSQL)

Advanced Grouping (SQL-99):

ROLLUP, CUBE, GROUPING SETS
Hierarchical and cross-tabulated aggregations

DDL (Data Definition):

CREATE TABLE, CREATE INDEX, CREATE VIEW, CREATE MATERIALIZED VIEW
ALTER TABLE, DROP TABLE, DROP INDEX
TRUNCATE TABLE

Constraints:

PRIMARY KEY, FOREIGN KEY, UNIQUE, CHECK
NOT NULL, DEFAULT
REFERENCES, CASCADE, RESTRICT

PostgreSQL Extensions:

JSON/JSONB operators (-> ->> #> #>> @> <@ ? ?| ?& #-)
RETURNING clause
FILTER clause
Array operators

Operators and Expressions:

AND, OR, NOT
IN, BETWEEN, LIKE, IS NULL, IS NOT NULL
CASE WHEN THEN ELSE END
NULLS FIRST, NULLS LAST

Functions:

String: SUBSTRING, TRIM, UPPER, LOWER, LENGTH, CONCAT
Conversion: CAST, CONVERT, COALESCE, NULLIF
Date/Time: NOW, CURRENT_DATE, CURRENT_TIME, CURRENT_TIMESTAMP

Code Snippets

The completion system includes 22 code snippets for rapid development:

Query Patterns:

sel       - Basic SELECT statement
selall    - SELECT * FROM table
selcount  - SELECT COUNT(*) with WHERE
seljoin   - SELECT with JOIN
selleft   - SELECT with LEFT JOIN
selgroup  - SELECT with GROUP BY and HAVING

DML Operations:

ins       - INSERT INTO with VALUES
inssel    - INSERT INTO with SELECT
upd       - UPDATE with SET and WHERE
del       - DELETE FROM with WHERE

DDL Operations:

cretbl    - CREATE TABLE with columns
creidx    - CREATE INDEX
altertbl  - ALTER TABLE ADD COLUMN
droptbl   - DROP TABLE IF EXISTS
trunc     - TRUNCATE TABLE

Advanced Features:

cte       - Common Table Expression (WITH)
cterec    - Recursive CTE
case      - CASE expression
casecol   - CASE on column value
window    - Window function with PARTITION BY
merge     - MERGE statement with MATCHED clauses
union     - UNION query
exists    - EXISTS subquery
subq      - Subquery template

Each snippet uses placeholder variables (${1}, ${2}, etc.) for easy tab navigation.

Function Signatures

Signature help is provided for these SQL function categories:

Aggregate Functions:

COUNT(expression)           - Count rows matching criteria
SUM(expression)             - Sum numeric values
AVG(expression)             - Calculate average
MIN(expression)             - Find minimum value
MAX(expression)             - Find maximum value

Window Functions:

ROW_NUMBER() OVER (...)     - Sequential row numbers
RANK() OVER (...)           - Ranks with gaps for ties
DENSE_RANK() OVER (...)     - Ranks without gaps
NTILE(buckets) OVER (...)   - Divide into N groups
LAG(expr, offset, default)  - Access previous row
LEAD(expr, offset, default) - Access next row
FIRST_VALUE(expr) OVER(...) - First value in window
LAST_VALUE(expr) OVER(...)  - Last value in window

String Functions:

SUBSTRING(string, start, length) - Extract substring
TRIM([spec] chars FROM string)   - Remove leading/trailing chars
UPPER(string)                    - Convert to uppercase
LOWER(string)                    - Convert to lowercase
LENGTH(string)                   - String length
CONCAT(str1, str2, ...)         - Concatenate strings

Null Handling:

COALESCE(val1, val2, ...)       - First non-null value
NULLIF(expr1, expr2)             - NULL if equal, else expr1

Type Conversion:

CAST(expression AS type)         - Type conversion

Performance and Limits

The LSP server includes built-in safeguards for stability:

Rate Limiting:

  • 100 requests per second maximum (RateLimitRequests)
  • 1-second rolling window (RateLimitWindow)
  • Automatic recovery after window expires
  • Client receives RequestCancelled (-32800) when exceeded

Message Size Limits:

  • MaxContentLength: 10MB per JSON-RPC message
  • MaxDocumentSize: 5MB per SQL document
  • Oversized documents skip validation with warning
  • Documents remain open but diagnostics disabled

Request Timeout:

  • 30 seconds per request (RequestTimeout)
  • Prevents hanging on malformed SQL
  • Long-running parses automatically cancelled

Memory Management:

  • GoSQLX object pooling for parser efficiency
  • Document content copied to prevent races
  • Automatic cleanup on document close

Performance Characteristics:

  • Parsing: <1ms for typical queries, <10ms for complex CTEs
  • Completion: <5ms for 100+ items with filtering
  • Formatting: <10ms for documents up to 1000 lines
  • Hover: <1ms for keyword lookup
  • Validation: <50ms for complex multi-statement documents

Error Handling

The server provides robust error handling throughout:

Position Extraction:

  • Structured errors from GoSQLX with line/column info
  • Regex fallback for unstructured error messages
  • Multiple patterns: "line X, column Y", "[X:Y]", "position N"
  • Conversion from absolute position to line/column

Error Codes:

  • JSON-RPC standard codes (-32700 to -32603)
  • LSP-specific codes (-32002, -32800 to -32803)
  • GoSQLX error codes propagated to diagnostics
  • Categorized by severity (Error, Warning, Info, Hint)

Diagnostic Features:

  • Precise error ranges for IDE underlining
  • Error code display in hover
  • Related information for multi-location errors
  • Automatic clearing on document close

Graceful Degradation:

  • Parse errors don't crash server
  • Malformed requests handled with error responses
  • Unknown methods return MethodNotFound
  • Oversized documents skip validation

Thread Safety

All components are designed for safe concurrent operation:

Server Level:

  • Write mutex for JSON-RPC output serialization
  • Rate limiting mutex for request counting
  • Atomic operations for rate limit counter

Document Manager:

  • Read/write mutex for document map
  • Read locks for Get/GetContent (concurrent reads)
  • Write locks for Open/Update/Close (exclusive writes)
  • Document copies returned to prevent races

Handler:

  • Stateless request processing
  • No shared mutable state
  • Keywords instance is read-only after construction
  • Safe for concurrent request handling

Logging and Debugging

The server supports comprehensive logging for debugging:

Log Levels:

  • Startup/Shutdown events
  • Received requests with method names
  • Sent responses with byte counts
  • Parse errors with content snippets
  • Rate limit violations
  • Document lifecycle events
  • Validation results (diagnostic counts)

Log Configuration:

  • Logger must write to file or stderr (never stdout)
  • Stdout is reserved for LSP protocol communication
  • Use --log flag with gosqlx CLI for file logging
  • Nil logger disables all logging (production use)

Example logging setup:

logFile, _ := os.Create("/tmp/gosqlx-lsp.log")
logger := log.New(logFile, "[LSP] ", log.LstdFlags|log.Lshortfile)
server := lsp.NewStdioServer(logger)

Protocol Compliance

The implementation conforms to LSP 3.17 specification:

Lifecycle:

  • initialize → initialize result with capabilities
  • initialized notification
  • shutdown request
  • exit notification

Text Synchronization:

  • Full and incremental sync modes
  • Version tracking
  • Open/Change/Close/Save notifications

Diagnostics:

  • publishDiagnostics notification
  • Version-tagged diagnostics
  • Multiple diagnostics per document
  • Automatic clearing on close

Code Intelligence:

  • hover request/response
  • completion request/response
  • formatting request/response
  • documentSymbol request/response
  • signatureHelp request/response
  • codeAction request/response

Error Handling:

  • Standard JSON-RPC 2.0 error responses
  • Error codes per specification
  • Detailed error messages
  • Error data field for additional context

Testing

The LSP implementation includes comprehensive tests:

Unit Tests:

  • Protocol message parsing
  • Document state management
  • Position/offset conversions
  • Error extraction patterns

Integration Tests:

  • Full request/response cycles
  • Multi-document scenarios
  • Concurrent request handling
  • Rate limiting behavior

Benchmark Tests:

  • Handler performance under load
  • Document update performance
  • Completion latency
  • Parse and validation speed

See pkg/lsp/*_test.go for test suite details.

For more information about the LSP server and GoSQLX features:

  • docs/LSP_GUIDE.md - Complete LSP server setup and IDE integration guide
  • docs/LINTING_RULES.md - All linting rules (L001-L010) reference
  • docs/CONFIGURATION.md - Configuration file (.gosqlx.yml) documentation
  • docs/USAGE_GUIDE.md - Comprehensive GoSQLX usage guide
  • docs/SQL_COMPATIBILITY.md - SQL dialect compatibility matrix

Standards and References

Language Server Protocol:

https://microsoft.github.io/language-server-protocol/

JSON-RPC 2.0 Specification:

https://www.jsonrpc.org/specification

SQL Standards:

  • SQL-92 (ISO/IEC 9075:1992)
  • SQL-99 (ISO/IEC 9075:1999) - Window functions, CTEs
  • SQL:2003 (ISO/IEC 9075:2003) - MERGE, XML
  • SQL:2011 (ISO/IEC 9075:2011) - Temporal features

GoSQLX Project:

https://github.com/ajitpratap0/GoSQLX

Package lsp implements the Language Server Protocol (LSP) server for GoSQLX.

The LSP server provides comprehensive SQL code intelligence features for IDEs and text editors, including real-time diagnostics, formatting, completion, and navigation capabilities.

Protocol Implementation

This file defines the LSP protocol types and structures according to the Language Server Protocol specification (version 3.17). It provides complete type definitions for:

  • JSON-RPC 2.0 message structures (Request, Response, Notification)
  • LSP lifecycle messages (Initialize, Initialized, Shutdown, Exit)
  • Text document synchronization (didOpen, didChange, didClose, didSave)
  • Code intelligence features (Completion, Hover, Formatting, etc.)
  • Diagnostic publishing (Errors, Warnings, Information)

Error Codes

The package defines standard JSON-RPC 2.0 error codes:

  • ParseError (-32700): Invalid JSON received
  • InvalidRequest (-32600): Invalid JSON-RPC request
  • MethodNotFound (-32601): Method not supported
  • InvalidParams (-32602): Invalid method parameters
  • InternalError (-32603): Internal server error

And LSP-specific error codes:

  • ServerNotInitialized (-32002): Server not yet initialized
  • RequestCancelled (-32800): Request cancelled by client
  • ContentModified (-32801): Content modified during operation
  • RequestFailed (-32803): Request failed

Usage

This package is typically not used directly. Instead, use the Server type from server.go to create and run an LSP server instance.

Index

Constants

View Source
const (
	// JSON-RPC standard error codes
	ParseError     = -32700
	InvalidRequest = -32600
	MethodNotFound = -32601
	InvalidParams  = -32602
	InternalError  = -32603

	// LSP-specific error codes
	ServerNotInitialized = -32002
	UnknownErrorCode     = -32001
	RequestCancelled     = -32800
	ContentModified      = -32801
	ServerCancelled      = -32802
	RequestFailed        = -32803
)

Error codes

View Source
const (
	// MaxContentLength limits the size of a single LSP message (10MB)
	MaxContentLength = 10 * 1024 * 1024
	// MaxDocumentSize limits the size of SQL documents (5MB)
	MaxDocumentSize = 5 * 1024 * 1024
	// RateLimitRequests is the max requests per rate limit window
	RateLimitRequests = 100
	// RateLimitWindow is the time window for rate limiting
	RateLimitWindow = time.Second
	// RequestTimeout limits how long a request can take
	RequestTimeout = 30 * time.Second
)

Server configuration constants

Variables

This section is empty.

Functions

This section is empty.

Types

type ClientCapabilities

type ClientCapabilities struct {
	TextDocument TextDocumentClientCapabilities `json:"textDocument,omitempty"`
}

ClientCapabilities describes the client's capabilities

type CodeAction

type CodeAction struct {
	Title       string         `json:"title"`
	Kind        CodeActionKind `json:"kind,omitempty"`
	Diagnostics []Diagnostic   `json:"diagnostics,omitempty"`
	IsPreferred bool           `json:"isPreferred,omitempty"`
	Edit        *WorkspaceEdit `json:"edit,omitempty"`
	Command     *Command       `json:"command,omitempty"`
}

CodeAction represents a code action

type CodeActionContext

type CodeActionContext struct {
	Diagnostics []Diagnostic     `json:"diagnostics"`
	Only        []CodeActionKind `json:"only,omitempty"`
}

CodeActionContext contains additional diagnostic information

type CodeActionKind

type CodeActionKind string

CodeActionKind represents the kind of code action

const (
	CodeActionQuickFix       CodeActionKind = "quickfix"
	CodeActionRefactor       CodeActionKind = "refactor"
	CodeActionSource         CodeActionKind = "source"
	CodeActionSourceOrganize CodeActionKind = "source.organizeImports"
)

type CodeActionOptions

type CodeActionOptions struct {
	CodeActionKinds []CodeActionKind `json:"codeActionKinds,omitempty"`
}

CodeActionOptions describes code action options

type CodeActionParams

type CodeActionParams struct {
	TextDocument TextDocumentIdentifier `json:"textDocument"`
	Range        Range                  `json:"range"`
	Context      CodeActionContext      `json:"context"`
}

CodeActionParams is the parameters for textDocument/codeAction

type Command

type Command struct {
	Title     string        `json:"title"`
	Command   string        `json:"command"`
	Arguments []interface{} `json:"arguments,omitempty"`
}

Command represents a command to be executed

type CompletionClientCapabilities

type CompletionClientCapabilities struct {
	DynamicRegistration bool `json:"dynamicRegistration,omitempty"`
	CompletionItem      *struct {
		SnippetSupport bool `json:"snippetSupport,omitempty"`
	} `json:"completionItem,omitempty"`
}

CompletionClientCapabilities describes completion capabilities

type CompletionContext

type CompletionContext struct {
	TriggerKind      CompletionTriggerKind `json:"triggerKind"`
	TriggerCharacter string                `json:"triggerCharacter,omitempty"`
}

CompletionContext provides additional information about the context

type CompletionItem

type CompletionItem struct {
	Label            string             `json:"label"`
	Kind             CompletionItemKind `json:"kind,omitempty"`
	Detail           string             `json:"detail,omitempty"`
	Documentation    interface{}        `json:"documentation,omitempty"`
	InsertText       string             `json:"insertText,omitempty"`
	InsertTextFormat InsertTextFormat   `json:"insertTextFormat,omitempty"`
}

CompletionItem represents a completion suggestion

type CompletionItemKind

type CompletionItemKind int

CompletionItemKind defines the kind of completion item

const (
	TextCompletion     CompletionItemKind = 1
	MethodCompletion   CompletionItemKind = 2
	FunctionCompletion CompletionItemKind = 3
	KeywordCompletion  CompletionItemKind = 14
	SnippetCompletion  CompletionItemKind = 15
)

type CompletionList

type CompletionList struct {
	IsIncomplete bool             `json:"isIncomplete"`
	Items        []CompletionItem `json:"items"`
}

CompletionList represents a list of completion items

type CompletionOptions

type CompletionOptions struct {
	TriggerCharacters []string `json:"triggerCharacters,omitempty"`
	ResolveProvider   bool     `json:"resolveProvider,omitempty"`
}

CompletionOptions describes completion options

type CompletionParams

type CompletionParams struct {
	TextDocumentPositionParams
	Context *CompletionContext `json:"context,omitempty"`
}

CompletionParams describes completion request parameters

type CompletionTriggerKind

type CompletionTriggerKind int

CompletionTriggerKind describes how completion was triggered

const (
	// Invoked means completion was invoked explicitly
	Invoked CompletionTriggerKind = 1
	// TriggerCharacter means completion was triggered by a character
	TriggerCharacter CompletionTriggerKind = 2
	// TriggerForIncompleteCompletions means re-triggered for incomplete completions
	TriggerForIncompleteCompletions CompletionTriggerKind = 3
)

type Diagnostic

type Diagnostic struct {
	Range              Range                          `json:"range"`
	Severity           DiagnosticSeverity             `json:"severity,omitempty"`
	Code               interface{}                    `json:"code,omitempty"`
	Source             string                         `json:"source,omitempty"`
	Message            string                         `json:"message"`
	RelatedInformation []DiagnosticRelatedInformation `json:"relatedInformation,omitempty"`
}

Diagnostic represents a diagnostic (error, warning, etc.)

type DiagnosticRelatedInformation

type DiagnosticRelatedInformation struct {
	Location Location `json:"location"`
	Message  string   `json:"message"`
}

DiagnosticRelatedInformation provides additional context

type DiagnosticSeverity

type DiagnosticSeverity int

DiagnosticSeverity represents the severity of a diagnostic

const (
	// SeverityError reports an error
	SeverityError DiagnosticSeverity = 1
	// SeverityWarning reports a warning
	SeverityWarning DiagnosticSeverity = 2
	// SeverityInformation reports information
	SeverityInformation DiagnosticSeverity = 3
	// SeverityHint reports a hint
	SeverityHint DiagnosticSeverity = 4
)

type DidChangeTextDocumentParams

type DidChangeTextDocumentParams struct {
	TextDocument   VersionedTextDocumentIdentifier  `json:"textDocument"`
	ContentChanges []TextDocumentContentChangeEvent `json:"contentChanges"`
}

DidChangeTextDocumentParams is sent when a document changes

type DidCloseTextDocumentParams

type DidCloseTextDocumentParams struct {
	TextDocument TextDocumentIdentifier `json:"textDocument"`
}

DidCloseTextDocumentParams is sent when a document is closed

type DidOpenTextDocumentParams

type DidOpenTextDocumentParams struct {
	TextDocument TextDocumentItem `json:"textDocument"`
}

DidOpenTextDocumentParams is sent when a document is opened

type DidSaveTextDocumentParams

type DidSaveTextDocumentParams struct {
	TextDocument TextDocumentIdentifier `json:"textDocument"`
	Text         string                 `json:"text,omitempty"`
}

DidSaveTextDocumentParams is sent when a document is saved

type Document

type Document struct {
	URI        string
	LanguageID string
	Version    int
	Content    string
	Lines      []string // Cached line splits
}

Document represents an open SQL document with its current state.

Document stores all information needed to process LSP requests for a single SQL file. It maintains the current content, version, and metadata.

Fields:

  • URI: Document identifier (file:// URI)
  • LanguageID: Language identifier (typically "sql")
  • Version: Monotonically increasing version number
  • Content: Current full text content
  • Lines: Cached line-split content for efficient position operations

The Lines field is automatically synchronized with Content to avoid repeated string splitting operations.

func (*Document) GetWordAtPosition

func (doc *Document) GetWordAtPosition(pos Position) string

GetWordAtPosition returns the word at the given position.

This method extracts the identifier or keyword at a specific cursor position, which is used for hover documentation and completion filtering.

The method uses rune-based indexing to properly handle UTF-8 encoded SQL identifiers that may contain international characters.

Word boundaries are defined as:

  • Start: Beginning of line or non-word character
  • End: End of line or non-word character
  • Word characters: A-Z, a-z, 0-9, underscore

Parameters:

  • pos: The cursor position (0-based line and character indices)

Returns:

  • The word at the position, or empty string if:
  • Position is out of bounds
  • No word character at position
  • Position is in whitespace

Example:

doc.Content = "SELECT name FROM users"
word := doc.GetWordAtPosition(Position{Line: 0, Character: 9})
// Returns: "name"

This method is safe for concurrent use as it operates on document fields without modifying state.

type DocumentFormattingParams

type DocumentFormattingParams struct {
	TextDocument TextDocumentIdentifier `json:"textDocument"`
	Options      FormattingOptions      `json:"options"`
}

DocumentFormattingParams describes formatting request parameters

type DocumentManager

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

DocumentManager manages open SQL documents in a thread-safe manner.

DocumentManager provides centralized document state management for the LSP server. It handles document lifecycle events (open, change, close) and maintains the current content and version for each document.

Thread Safety

All operations are protected by a read/write mutex:

  • Read operations (Get, GetContent): Use read lock for concurrent access
  • Write operations (Open, Update, Close): Use write lock for exclusive access

This ensures safe concurrent access from multiple LSP request handlers.

Document Lifecycle

Documents follow the LSP document lifecycle:

  1. Open: Document opened in editor (textDocument/didOpen)
  2. Update: Content changes as user edits (textDocument/didChange)
  3. Close: Document closed in editor (textDocument/didClose)

Synchronization Modes

The manager supports both synchronization modes:

  • Full sync: Entire document content sent on each change
  • Incremental sync: Only changed portions sent (more efficient)

Document Versioning

Each document has a version number that increments with changes. This enables the server to:

  • Detect stale diagnostics
  • Handle out-of-order updates
  • Verify diagnostic freshness

Content Caching

Documents cache their line-split content to optimize:

  • Position-to-offset conversions
  • Word extraction for hover and completion
  • Incremental change application

func NewDocumentManager

func NewDocumentManager() *DocumentManager

NewDocumentManager creates a new document manager.

This constructor initializes a DocumentManager with an empty document map. The returned manager is ready to handle document lifecycle events from LSP clients.

Returns:

  • *DocumentManager: A new document manager instance

Thread Safety: The returned DocumentManager is fully thread-safe and ready for concurrent use by multiple LSP request handlers.

Usage:

dm := NewDocumentManager()
dm.Open("file:///query.sql", "sql", 1, "SELECT * FROM users")

Typically, this is called once when creating the LSP server, not for each document operation.

func (*DocumentManager) Close

func (dm *DocumentManager) Close(uri string)

Close removes a document from the manager.

This method is called when the client sends a textDocument/didClose notification. It removes the document from the internal map and releases associated resources.

Parameters:

  • uri: Document URI to close and remove

Thread Safety: This method uses a write lock to safely remove documents concurrently from multiple goroutines.

After closing a document, the server typically sends an empty diagnostics notification to clear any error markers in the editor:

dm.Close("file:///query.sql")
server.SendNotification("textDocument/publishDiagnostics", PublishDiagnosticsParams{
    URI:         "file:///query.sql",
    Diagnostics: []Diagnostic{},
})

If the document doesn't exist, this method does nothing (safe to call redundantly).

Once closed, the document must be re-opened with Open() before it can be accessed again. Attempting to Update() or Get() a closed document will fail.

func (*DocumentManager) Get

func (dm *DocumentManager) Get(uri string) (*Document, bool)

Get retrieves a copy of a document to avoid race conditions The returned document is a snapshot and modifications won't affect the original

func (*DocumentManager) GetContent

func (dm *DocumentManager) GetContent(uri string) (string, bool)

GetContent retrieves a document's content

func (*DocumentManager) Open

func (dm *DocumentManager) Open(uri, languageID string, version int, content string)

Open adds a document to the manager.

This method is called when the client sends a textDocument/didOpen notification. It stores the initial document state including URI, language, version, and content.

Parameters:

  • uri: Document URI (e.g., "file:///path/to/query.sql")
  • languageID: Language identifier (typically "sql")
  • version: Initial version number (starts at 1, increments with changes)
  • content: Full document text content

Thread Safety: This method uses a write lock to safely add documents concurrently from multiple goroutines.

The document's content is cached in both raw form (Content) and split into lines (Lines) for efficient position-to-offset conversions.

Example:

dm.Open("file:///query.sql", "sql", 1, "SELECT * FROM users WHERE active = true")

If a document with the same URI already exists, it will be replaced with the new content and version.

func (*DocumentManager) Update

func (dm *DocumentManager) Update(uri string, version int, changes []TextDocumentContentChangeEvent)

Update updates a document's content.

This method is called when the client sends a textDocument/didChange notification. It applies content changes to an existing document and updates its version number.

Parameters:

  • uri: Document URI to update
  • version: New version number (should be greater than current version)
  • changes: Array of content changes to apply

Thread Safety: This method uses a write lock to safely update documents concurrently from multiple goroutines.

The method supports two synchronization modes:

Full Sync (change.Range == nil):

  • The entire document is replaced with change.Text
  • Simple and robust, but sends more data over the network

Incremental Sync (change.Range != nil):

  • Only the specified range is replaced with change.Text
  • More efficient for large documents with small edits
  • Requires proper position-to-offset conversion

Example - Full sync:

dm.Update("file:///query.sql", 2, []TextDocumentContentChangeEvent{
    {Text: "SELECT id, name FROM users WHERE active = true"},
})

Example - Incremental sync:

dm.Update("file:///query.sql", 3, []TextDocumentContentChangeEvent{
    {
        Range: &Range{Start: Position{Line: 0, Character: 7}, End: Position{Line: 0, Character: 8}},
        Text:  "*",
    },
})

If the document doesn't exist, this method does nothing. The document must first be opened with Open() before it can be updated.

After applying changes, the Lines cache is automatically rebuilt for efficient subsequent operations.

type DocumentSymbol

type DocumentSymbol struct {
	Name           string           `json:"name"`
	Detail         string           `json:"detail,omitempty"`
	Kind           SymbolKind       `json:"kind"`
	Range          Range            `json:"range"`
	SelectionRange Range            `json:"selectionRange"`
	Children       []DocumentSymbol `json:"children,omitempty"`
}

DocumentSymbol represents a symbol in a document (hierarchical)

type DocumentSymbolParams

type DocumentSymbolParams struct {
	TextDocument TextDocumentIdentifier `json:"textDocument"`
}

DocumentSymbolParams is the parameters for textDocument/documentSymbol

type FormattingOptions

type FormattingOptions struct {
	TabSize                int  `json:"tabSize"`
	InsertSpaces           bool `json:"insertSpaces"`
	TrimTrailingWhitespace bool `json:"trimTrailingWhitespace,omitempty"`
	InsertFinalNewline     bool `json:"insertFinalNewline,omitempty"`
	TrimFinalNewlines      bool `json:"trimFinalNewlines,omitempty"`
}

FormattingOptions describes formatting options

type Handler

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

Handler processes LSP requests and notifications.

Handler implements all LSP protocol handlers for the GoSQLX language server. It coordinates between the LSP protocol layer, document management, and the GoSQLX SQL parser to provide comprehensive code intelligence features.

Supported LSP Methods

Lifecycle:

  • initialize: Server initialization and capability negotiation
  • initialized: Confirmation of successful initialization
  • shutdown: Graceful shutdown preparation
  • exit: Final shutdown notification

Text Synchronization:

  • textDocument/didOpen: Document opened in editor
  • textDocument/didChange: Document content modified (incremental sync supported)
  • textDocument/didClose: Document closed in editor
  • textDocument/didSave: Document saved to disk

Code Intelligence:

  • textDocument/hover: Show keyword documentation (60+ SQL keywords)
  • textDocument/completion: Auto-complete keywords and snippets (100+ items)
  • textDocument/formatting: Format SQL with intelligent indentation
  • textDocument/documentSymbol: Outline view of SQL statements
  • textDocument/signatureHelp: Function parameter help (20+ functions)
  • textDocument/codeAction: Quick fixes for common errors

Diagnostics:

  • textDocument/publishDiagnostics: Real-time syntax error reporting

Keyword Documentation

The handler provides hover documentation for SQL keywords including:

  • Core DML: SELECT, INSERT, UPDATE, DELETE, MERGE
  • DDL: CREATE, ALTER, DROP, TRUNCATE
  • JOINs: INNER, LEFT, RIGHT, FULL OUTER, CROSS, NATURAL
  • Clauses: WHERE, GROUP BY, HAVING, ORDER BY, LIMIT, OFFSET
  • CTEs: WITH, RECURSIVE
  • Set Operations: UNION, EXCEPT, INTERSECT
  • Window Functions: ROW_NUMBER, RANK, DENSE_RANK, LAG, LEAD, etc.
  • Aggregates: COUNT, SUM, AVG, MIN, MAX
  • Advanced: ROLLUP, CUBE, GROUPING SETS, PARTITION BY

Completion Features

Auto-completion includes:

  • 100+ SQL keywords with context-appropriate filtering
  • 22 code snippets for common SQL patterns
  • Trigger characters: space, dot, opening parenthesis
  • Prefix-based filtering for fast results

Snippet examples:

  • "sel" → Complete SELECT statement template
  • "cte" → Common Table Expression with RECURSIVE option
  • "window" → Window function with PARTITION BY and ORDER BY
  • "merge" → MERGE statement with MATCHED/NOT MATCHED clauses

Error Handling

The handler provides sophisticated error reporting:

  • Position extraction from GoSQLX structured errors
  • Fallback regex patterns for unstructured error messages
  • Error code propagation for diagnostic categorization
  • Precise error ranges for IDE underlining

Document Size Limits

Documents are subject to size limits for performance:

  • MaxDocumentSize (5MB): Documents larger than this skip validation
  • Warning message sent to client for oversized documents
  • Documents still opened but diagnostics disabled

Thread Safety

Handler operations are thread-safe through:

  • DocumentManager's read/write locking
  • Immutable keyword and snippet data structures
  • No shared mutable state between requests

func NewHandler

func NewHandler(server *Server) *Handler

NewHandler creates a new LSP request handler.

This constructor initializes the handler with a reference to the server and sets up the SQL keywords database for hover documentation and completion.

The handler uses DialectGeneric for maximum SQL compatibility across PostgreSQL, MySQL, SQL Server, Oracle, and SQLite dialects.

Parameters:

  • server: The LSP server instance that owns this handler

Returns a fully initialized Handler ready to process LSP requests.

func (*Handler) HandleNotification

func (h *Handler) HandleNotification(method string, params json.RawMessage)

HandleNotification processes an LSP notification

func (*Handler) HandleRequest

func (h *Handler) HandleRequest(method string, params json.RawMessage) (interface{}, error)

HandleRequest processes an LSP request and returns a result

type Hover

type Hover struct {
	Contents MarkupContent `json:"contents"`
	Range    *Range        `json:"range,omitempty"`
}

Hover represents hover information

type HoverClientCapabilities

type HoverClientCapabilities struct {
	DynamicRegistration bool     `json:"dynamicRegistration,omitempty"`
	ContentFormat       []string `json:"contentFormat,omitempty"`
}

HoverClientCapabilities describes hover capabilities

type InitializeParams

type InitializeParams struct {
	ProcessID             int                `json:"processId"`
	RootURI               string             `json:"rootUri"`
	RootPath              string             `json:"rootPath,omitempty"`
	Capabilities          ClientCapabilities `json:"capabilities"`
	InitializationOptions interface{}        `json:"initializationOptions,omitempty"`
}

InitializeParams contains initialization options

type InitializeResult

type InitializeResult struct {
	Capabilities ServerCapabilities `json:"capabilities"`
	ServerInfo   *ServerInfo        `json:"serverInfo,omitempty"`
}

InitializeResult is the response to initialize

type InsertTextFormat

type InsertTextFormat int

InsertTextFormat defines the format of the insert text

const (
	PlainTextFormat InsertTextFormat = 1
	SnippetFormat   InsertTextFormat = 2
)

type Location

type Location struct {
	URI   string `json:"uri"`
	Range Range  `json:"range"`
}

Location represents a location in a document

type MarkupContent

type MarkupContent struct {
	Kind  MarkupKind `json:"kind"`
	Value string     `json:"value"`
}

MarkupContent represents markup content

type MarkupKind

type MarkupKind string

MarkupKind describes the markup type

const (
	// PlainText is plain text
	PlainText MarkupKind = "plaintext"
	// Markdown is markdown
	Markdown MarkupKind = "markdown"
)

type MessageType

type MessageType int

MessageType represents the type of message to show

const (
	// MessageError is an error message
	MessageError MessageType = 1
	// MessageWarning is a warning message
	MessageWarning MessageType = 2
	// MessageInfo is an info message
	MessageInfo MessageType = 3
	// MessageLog is a log message
	MessageLog MessageType = 4
)

type Notification

type Notification struct {
	JSONRPC string          `json:"jsonrpc"`
	Method  string          `json:"method"`
	Params  json.RawMessage `json:"params,omitempty"`
}

Notification represents a JSON-RPC 2.0 notification (request without ID).

A notification is a special type of request that does not expect a response. It has no ID field, and the server will not send a response. Notifications are used for events that the client sends to the server without needing acknowledgment, such as document change notifications.

Example notification:

{
  "jsonrpc": "2.0",
  "method": "textDocument/didChange",
  "params": { "textDocument": { "uri": "file:///query.sql", "version": 2 }, "contentChanges": [...] }
}

type ParameterInformation

type ParameterInformation struct {
	Label         interface{} `json:"label"` // string or [int, int]
	Documentation interface{} `json:"documentation,omitempty"`
}

ParameterInformation represents a parameter of a function signature

type Position

type Position struct {
	Line      int `json:"line"`
	Character int `json:"character"`
}

Position in a text document

type PublishDiagnosticsClientCapabilities

type PublishDiagnosticsClientCapabilities struct {
	RelatedInformation bool `json:"relatedInformation,omitempty"`
}

PublishDiagnosticsClientCapabilities describes diagnostics capabilities

type PublishDiagnosticsParams

type PublishDiagnosticsParams struct {
	URI         string       `json:"uri"`
	Version     int          `json:"version,omitempty"`
	Diagnostics []Diagnostic `json:"diagnostics"`
}

PublishDiagnosticsParams is sent to publish diagnostics

type Range

type Range struct {
	Start Position `json:"start"`
	End   Position `json:"end"`
}

Range in a text document

type Request

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

Request represents a JSON-RPC 2.0 request message.

A request is a message sent from the client to the server expecting a response. It contains a unique ID to correlate the request with its response, a method name identifying the operation to perform, and optional parameters for the method.

The JSONRPC field must always be "2.0" per the JSON-RPC 2.0 specification.

Example request:

{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "textDocument/hover",
  "params": { "textDocument": { "uri": "file:///query.sql" }, "position": { "line": 0, "character": 5 } }
}

type Response

type Response struct {
	JSONRPC string         `json:"jsonrpc"`
	ID      interface{}    `json:"id,omitempty"`
	Result  interface{}    `json:"result,omitempty"`
	Error   *ResponseError `json:"error,omitempty"`
}

Response represents a JSON-RPC 2.0 response message.

A response is sent from the server back to the client in reply to a request. It contains the same ID as the request to correlate them. Either Result or Error will be set, but never both.

The JSONRPC field must always be "2.0" per the JSON-RPC 2.0 specification.

Example successful response:

{
  "jsonrpc": "2.0",
  "id": 1,
  "result": { "contents": { "kind": "markdown", "value": "**SELECT** - Retrieves data..." } }
}

Example error response:

{
  "jsonrpc": "2.0",
  "id": 1,
  "error": { "code": -32601, "message": "Method not found" }
}

type ResponseError

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

ResponseError represents a JSON-RPC 2.0 error.

This type carries error information when a request fails. The Code field contains a numeric error code (see error code constants), Message provides a human-readable description, and Data optionally contains additional context.

Standard error codes are defined as package constants (ParseError, InvalidRequest, etc.).

type SaveOptions

type SaveOptions struct {
	IncludeText bool `json:"includeText,omitempty"`
}

SaveOptions describes save options

type Server

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

Server represents the LSP server instance.

Server implements the Language Server Protocol for SQL code intelligence. It manages client-server communication over stdin/stdout using JSON-RPC 2.0, handles document lifecycle events, and coordinates all LSP protocol handlers.

Features

The server provides the following capabilities:

  • Real-time syntax validation with diagnostics (textDocument/publishDiagnostics)
  • SQL code formatting with intelligent indentation (textDocument/formatting)
  • Keyword hover documentation for 60+ SQL keywords (textDocument/hover)
  • Auto-completion with 100+ keywords and 22 snippets (textDocument/completion)
  • Document outline and symbol navigation (textDocument/documentSymbol)
  • Function signature help for 20+ SQL functions (textDocument/signatureHelp)
  • Quick fixes and code actions (textDocument/codeAction)

Architecture

The server uses a multi-component architecture:

  • Server: Main server loop and JSON-RPC message handling
  • DocumentManager: Thread-safe document state management
  • Handler: LSP protocol request and notification processing

Concurrency

Server is designed for concurrent operation:

  • Thread-safe document management with read/write locks
  • Atomic rate limiting for request throttling
  • Synchronized write operations to prevent message corruption

Rate Limiting

Built-in rate limiting protects against request floods:

  • Maximum 100 requests per second (configurable via RateLimitRequests)
  • Automatic rate limit window reset
  • Client receives RequestCancelled error when limit exceeded

Message Size Limits

The server enforces size limits for stability:

  • MaxContentLength: 10MB per LSP message
  • MaxDocumentSize: 5MB per SQL document

Error Handling

Robust error handling throughout the server:

  • Malformed JSON-RPC messages handled gracefully
  • Position information extracted from GoSQLX errors
  • Structured errors with error codes for diagnostics

Example Usage

logger := log.New(os.Stderr, "[LSP] ", log.LstdFlags)
server := lsp.NewStdioServer(logger)
if err := server.Run(); err != nil {
    log.Fatal(err)
}

Or via the CLI:

./gosqlx lsp
./gosqlx lsp --log /tmp/lsp.log

IDE Integration

The server can be integrated with various editors:

VSCode - Add to settings.json:

{
  "gosqlx-lsp": {
    "command": "gosqlx",
    "args": ["lsp"],
    "filetypes": ["sql"]
  }
}

Neovim - Add to init.lua:

vim.api.nvim_create_autocmd("FileType", {
  pattern = "sql",
  callback = function()
    vim.lsp.start({
      name = "gosqlx-lsp",
      cmd = {"gosqlx", "lsp"}
    })
  end
})

Emacs (lsp-mode) - Add to init.el:

(require 'lsp-mode)
(add-to-list 'lsp-language-id-configuration '(sql-mode . "sql"))
(lsp-register-client
  (make-lsp-client :new-connection (lsp-stdio-connection '("gosqlx" "lsp"))
                   :major-modes '(sql-mode)
                   :server-id 'gosqlx-lsp))

See docs/LSP_GUIDE.md for comprehensive integration documentation.

func NewServer

func NewServer(reader io.Reader, writer io.Writer, logger *log.Logger) *Server

NewServer creates a new LSP server with custom input/output streams.

This constructor allows you to specify custom reader and writer for the JSON-RPC 2.0 communication. The server will read LSP messages from reader and write responses to writer.

Parameters:

  • reader: Input stream for receiving LSP messages (typically os.Stdin)
  • writer: Output stream for sending LSP responses (typically os.Stdout)
  • logger: Logger for server diagnostics (use io.Discard for silent operation)

The logger parameter can be nil, in which case logging will be disabled. For production deployments, it's recommended to provide a logger that writes to a file rather than stderr to avoid interfering with LSP communication.

Example:

logFile, _ := os.Create("/tmp/gosqlx-lsp.log")
logger := log.New(logFile, "[GoSQLX LSP] ", log.LstdFlags)
server := lsp.NewServer(os.Stdin, os.Stdout, logger)
defer logFile.Close()

Returns a fully initialized Server ready to call Run().

func NewStdioServer

func NewStdioServer(logger *log.Logger) *Server

NewStdioServer creates a new LSP server using stdin/stdout.

This is the standard constructor for LSP servers that communicate over standard input/output streams, which is the typical mode for editor integration.

The server reads LSP protocol messages from os.Stdin and writes responses to os.Stdout. This is the recommended way to create an LSP server for use with editors like VSCode, Neovim, and Emacs.

Parameters:

  • logger: Logger for server diagnostics. Should write to a file or os.Stderr, never to os.Stdout (which is reserved for LSP communication)

Example:

logFile, _ := os.Create("/tmp/gosqlx-lsp.log")
logger := log.New(logFile, "", log.LstdFlags)
server := lsp.NewStdioServer(logger)
if err := server.Run(); err != nil {
    logger.Fatal(err)
}

This is equivalent to:

NewServer(os.Stdin, os.Stdout, logger)

func (*Server) Documents

func (s *Server) Documents() *DocumentManager

Documents returns the server's document manager.

The DocumentManager provides access to all currently open SQL documents and their state. This method is primarily used internally by request handlers to access document content when processing LSP requests.

Returns:

  • *DocumentManager: The server's document manager instance

Thread Safety: The returned DocumentManager is thread-safe and can be used concurrently from multiple request handlers.

Usage:

doc, ok := server.Documents().Get("file:///query.sql")
if ok {
    content := doc.Content
    // Process document content
}

func (*Server) Logger

func (s *Server) Logger() *log.Logger

Logger returns the server's logger instance.

The logger is used for debugging and diagnostic output. It should write to a file or os.Stderr, never to os.Stdout (which is reserved for LSP protocol communication).

Returns:

  • *log.Logger: The server's logger, or a logger that discards output if the server was created with a nil logger

Thread Safety: The standard log.Logger is thread-safe and can be used concurrently from multiple goroutines.

Example:

server.Logger().Printf("Processing request: %s", method)

func (*Server) MaxDocumentSizeBytes

func (s *Server) MaxDocumentSizeBytes() int

MaxDocumentSizeBytes returns the maximum allowed document size

func (*Server) Run

func (s *Server) Run() error

Run starts the server's main loop and processes LSP messages.

This method blocks until the server receives an exit notification or encounters an unrecoverable error. It continuously reads LSP messages from the input stream, processes them, and sends responses.

The main loop:

  1. Reads a complete LSP message (headers + content)
  2. Validates message size against MaxContentLength
  3. Applies rate limiting (RateLimitRequests per RateLimitWindow)
  4. Parses JSON-RPC 2.0 structure
  5. Dispatches to appropriate handler
  6. Sends response or error back to client

Shutdown Sequence:

The server follows the LSP shutdown protocol:

  1. Client sends "shutdown" request → Server responds with empty result
  2. Client sends "exit" notification → Server stops message loop
  3. Run() returns nil for clean shutdown

Error Handling:

The server handles various error conditions gracefully:

  • EOF on stdin: Assumes client disconnected, returns nil
  • Parse errors: Sends ParseError response, continues
  • Rate limit exceeded: Sends RequestCancelled error
  • Malformed JSON: Attempts to extract ID for error response
  • Unknown methods: Sends MethodNotFound error

Returns:

  • nil on clean shutdown (exit notification received)
  • nil on EOF (client disconnected)
  • error only for unexpected fatal conditions

Example:

server := lsp.NewStdioServer(logger)
if err := server.Run(); err != nil {
    log.Fatalf("LSP server error: %v", err)
}

func (*Server) SendNotification

func (s *Server) SendNotification(method string, params interface{})

SendNotification sends a notification to the client.

This method sends a JSON-RPC 2.0 notification (a request without an ID) to the client. Notifications are one-way messages that do not expect a response.

The server uses this method to push information to the client asynchronously, such as diagnostic results (textDocument/publishDiagnostics) or progress updates.

Parameters:

  • method: The LSP method name (e.g., "textDocument/publishDiagnostics")
  • params: The parameters object to send (will be JSON-marshaled)

Thread Safety: This method is thread-safe and can be called concurrently from multiple goroutines. Write operations are protected by a mutex.

Common notification methods:

  • "textDocument/publishDiagnostics": Send syntax errors to client
  • "window/showMessage": Display message to user
  • "window/logMessage": Log message in client

Example:

s.SendNotification("textDocument/publishDiagnostics", PublishDiagnosticsParams{
    URI:         "file:///query.sql",
    Diagnostics: diagnostics,
})

If params is nil, an empty notification without params will be sent. If marshaling params fails, the error is logged but no notification is sent.

func (*Server) SetShutdown

func (s *Server) SetShutdown()

SetShutdown marks the server for shutdown.

This method is called when the server receives an "exit" notification from the client. It sets an internal flag that causes the main message loop in Run() to terminate cleanly.

Thread Safety: This method is safe to call concurrently, though it's typically only called from the exit notification handler.

The shutdown sequence:

  1. Client sends "shutdown" request → Server responds with empty result
  2. Client sends "exit" notification → Server calls SetShutdown()
  3. Run() method checks shutdown flag and returns nil

This method does not immediately stop the server; it only marks it for shutdown. The actual termination occurs when the Run() loop checks the flag.

type ServerCapabilities

type ServerCapabilities struct {
	TextDocumentSync           *TextDocumentSyncOptions `json:"textDocumentSyncOptions,omitempty"`
	CompletionProvider         *CompletionOptions       `json:"completionProvider,omitempty"`
	HoverProvider              bool                     `json:"hoverProvider,omitempty"`
	DocumentFormattingProvider bool                     `json:"documentFormattingProvider,omitempty"`
	DocumentSymbolProvider     bool                     `json:"documentSymbolProvider,omitempty"`
	SignatureHelpProvider      *SignatureHelpOptions    `json:"signatureHelpProvider,omitempty"`
	CodeActionProvider         interface{}              `json:"codeActionProvider,omitempty"` // bool or CodeActionOptions
}

ServerCapabilities describes what the server can do

type ServerInfo

type ServerInfo struct {
	Name    string `json:"name"`
	Version string `json:"version,omitempty"`
}

ServerInfo provides information about the server

type ShowMessageParams

type ShowMessageParams struct {
	Type    MessageType `json:"type"`
	Message string      `json:"message"`
}

ShowMessageParams is used to show a message to the user

type ShutdownResult

type ShutdownResult struct{}

ShutdownResult is the result of a shutdown request

type SignatureHelp

type SignatureHelp struct {
	Signatures      []SignatureInformation `json:"signatures"`
	ActiveSignature int                    `json:"activeSignature,omitempty"`
	ActiveParameter int                    `json:"activeParameter,omitempty"`
}

SignatureHelp represents signature help information

type SignatureHelpOptions

type SignatureHelpOptions struct {
	TriggerCharacters   []string `json:"triggerCharacters,omitempty"`
	RetriggerCharacters []string `json:"retriggerCharacters,omitempty"`
}

SignatureHelpOptions describes signature help options

type SignatureInformation

type SignatureInformation struct {
	Label         string                 `json:"label"`
	Documentation interface{}            `json:"documentation,omitempty"`
	Parameters    []ParameterInformation `json:"parameters,omitempty"`
}

SignatureInformation represents a function signature

type SymbolInformation

type SymbolInformation struct {
	Name          string     `json:"name"`
	Kind          SymbolKind `json:"kind"`
	Location      Location   `json:"location"`
	ContainerName string     `json:"containerName,omitempty"`
}

SymbolInformation represents a symbol (flat list)

type SymbolKind

type SymbolKind int

SymbolKind represents the kind of symbol

const (
	SymbolFile        SymbolKind = 1
	SymbolModule      SymbolKind = 2
	SymbolNamespace   SymbolKind = 3
	SymbolPackage     SymbolKind = 4
	SymbolClass       SymbolKind = 5
	SymbolMethod      SymbolKind = 6
	SymbolProperty    SymbolKind = 7
	SymbolField       SymbolKind = 8
	SymbolConstructor SymbolKind = 9
	SymbolEnum        SymbolKind = 10
	SymbolInterface   SymbolKind = 11
	SymbolFunction    SymbolKind = 12
	SymbolVariable    SymbolKind = 13
	SymbolConstant    SymbolKind = 14
	SymbolString      SymbolKind = 15
	SymbolNumber      SymbolKind = 16
	SymbolBoolean     SymbolKind = 17
	SymbolArray       SymbolKind = 18
	SymbolObject      SymbolKind = 19
	SymbolKey         SymbolKind = 20
	SymbolNull        SymbolKind = 21
	SymbolEnumMember  SymbolKind = 22
	SymbolStruct      SymbolKind = 23
	SymbolEvent       SymbolKind = 24
	SymbolOperator    SymbolKind = 25
	SymbolTypeParam   SymbolKind = 26
)

type TextDocumentClientCapabilities

type TextDocumentClientCapabilities struct {
	Synchronization    *TextDocumentSyncClientCapabilities   `json:"synchronization,omitempty"`
	Completion         *CompletionClientCapabilities         `json:"completion,omitempty"`
	Hover              *HoverClientCapabilities              `json:"hover,omitempty"`
	PublishDiagnostics *PublishDiagnosticsClientCapabilities `json:"publishDiagnostics,omitempty"`
}

TextDocumentClientCapabilities describes text document capabilities

type TextDocumentContentChangeEvent

type TextDocumentContentChangeEvent struct {
	Range       *Range `json:"range,omitempty"`
	RangeLength int    `json:"rangeLength,omitempty"`
	Text        string `json:"text"`
}

TextDocumentContentChangeEvent describes a content change

type TextDocumentEdit

type TextDocumentEdit struct {
	TextDocument VersionedTextDocumentIdentifier `json:"textDocument"`
	Edits        []TextEdit                      `json:"edits"`
}

TextDocumentEdit represents an edit to a single document

type TextDocumentIdentifier

type TextDocumentIdentifier struct {
	URI string `json:"uri"`
}

TextDocumentIdentifier identifies a document

type TextDocumentItem

type TextDocumentItem struct {
	URI        string `json:"uri"`
	LanguageID string `json:"languageId"`
	Version    int    `json:"version"`
	Text       string `json:"text"`
}

TextDocumentItem represents a document

type TextDocumentPositionParams

type TextDocumentPositionParams struct {
	TextDocument TextDocumentIdentifier `json:"textDocument"`
	Position     Position               `json:"position"`
}

TextDocumentPositionParams identifies a position in a document

type TextDocumentSyncClientCapabilities

type TextDocumentSyncClientCapabilities struct {
	DynamicRegistration bool `json:"dynamicRegistration,omitempty"`
	WillSave            bool `json:"willSave,omitempty"`
	WillSaveWaitUntil   bool `json:"willSaveWaitUntil,omitempty"`
	DidSave             bool `json:"didSave,omitempty"`
}

TextDocumentSyncClientCapabilities describes sync capabilities

type TextDocumentSyncKind

type TextDocumentSyncKind int

TextDocumentSyncKind defines how the client syncs document changes

const (
	// SyncNone means documents should not be synced at all
	SyncNone TextDocumentSyncKind = 0
	// SyncFull means documents are synced by sending the full content
	SyncFull TextDocumentSyncKind = 1
	// SyncIncremental means documents are synced by sending incremental updates
	SyncIncremental TextDocumentSyncKind = 2
)

type TextDocumentSyncOptions

type TextDocumentSyncOptions struct {
	OpenClose bool                 `json:"openClose,omitempty"`
	Change    TextDocumentSyncKind `json:"change,omitempty"`
	Save      *SaveOptions         `json:"save,omitempty"`
}

TextDocumentSyncOptions describes how documents are synced

type TextEdit

type TextEdit struct {
	Range   Range  `json:"range"`
	NewText string `json:"newText"`
}

TextEdit represents a text edit

type VersionedTextDocumentIdentifier

type VersionedTextDocumentIdentifier struct {
	TextDocumentIdentifier
	Version int `json:"version"`
}

VersionedTextDocumentIdentifier identifies a specific version of a document

type WorkspaceEdit

type WorkspaceEdit struct {
	Changes         map[string][]TextEdit `json:"changes,omitempty"`
	DocumentChanges []TextDocumentEdit    `json:"documentChanges,omitempty"`
}

WorkspaceEdit represents changes to be applied to a workspace

Jump to

Keyboard shortcuts

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