256 lines
6.9 KiB
Go
256 lines
6.9 KiB
Go
// Copyright (c) 2026 Petr Balvín <opensource@petrbalvin.org> (https://petrbalvin.org)
|
|
// SPDX-License-Identifier: BSD-3-Clause
|
|
|
|
// Package lsp implements a Language Server Protocol server for GAsm. It
|
|
// speaks JSON-RPC 2.0 over any io.Reader/io.Writer pair (normally standard
|
|
// input/output) and provides completion, hover documentation, document
|
|
// symbols, diagnostics and semantic-token highlighting — all backed by the
|
|
// pure-Go lexer, parser, arch and lint packages. It is the vendor-neutral
|
|
// integration point: any LSP-capable editor can use it with no editor-specific
|
|
// plugin code.
|
|
package lsp
|
|
|
|
import "encoding/json"
|
|
|
|
// --- JSON-RPC 2.0 -----------------------------------------------------------
|
|
|
|
// rpcMessage is the common envelope for every JSON-RPC message.
|
|
type rpcMessage struct {
|
|
JSONRPC string `json:"jsonrpc"`
|
|
ID *json.RawMessage `json:"id,omitempty"`
|
|
Method string `json:"method,omitempty"`
|
|
Params json.RawMessage `json:"params,omitempty"`
|
|
Result any `json:"result,omitempty"`
|
|
Error *rpcError `json:"error,omitempty"`
|
|
}
|
|
|
|
type rpcError struct {
|
|
Code int `json:"code"`
|
|
Message string `json:"message"`
|
|
}
|
|
|
|
// JSON-RPC error codes used by LSP.
|
|
const (
|
|
errParse = -32700
|
|
errInvalidRequest = -32600
|
|
errMethodNotFound = -32601
|
|
errInvalidParams = -32602
|
|
errInternal = -32603
|
|
)
|
|
|
|
// --- LSP positions and ranges ----------------------------------------------
|
|
|
|
// Position is a zero-based line/character position, as LSP requires.
|
|
type Position struct {
|
|
Line int `json:"line"`
|
|
Character int `json:"character"`
|
|
}
|
|
|
|
// Range is a pair of positions.
|
|
type Range struct {
|
|
Start Position `json:"start"`
|
|
End Position `json:"end"`
|
|
}
|
|
|
|
// Location is a range within a document URI.
|
|
type Location struct {
|
|
URI string `json:"uri"`
|
|
Range Range `json:"range"`
|
|
}
|
|
|
|
// --- diagnostics ------------------------------------------------------------
|
|
|
|
// Diagnostic severities (LSP ordering: 1 = error).
|
|
const (
|
|
sevError = 1
|
|
sevWarning = 2
|
|
sevInformation = 3
|
|
sevHint = 4
|
|
)
|
|
|
|
// Diagnostic is one published finding.
|
|
type Diagnostic struct {
|
|
Range Range `json:"range"`
|
|
Severity int `json:"severity"`
|
|
Code string `json:"code,omitempty"`
|
|
Source string `json:"source,omitempty"`
|
|
Message string `json:"message"`
|
|
}
|
|
|
|
type publishDiagnosticsParams struct {
|
|
URI string `json:"uri"`
|
|
Diagnostics []Diagnostic `json:"diagnostics"`
|
|
}
|
|
|
|
// --- text document synchronisation -----------------------------------------
|
|
|
|
type textDocumentItem struct {
|
|
URI string `json:"uri"`
|
|
LanguageID string `json:"languageId"`
|
|
Version int `json:"version"`
|
|
Text string `json:"text"`
|
|
}
|
|
|
|
type didOpenParams struct {
|
|
TextDocument textDocumentItem `json:"textDocument"`
|
|
}
|
|
|
|
type versionedTextDocumentIdentifier struct {
|
|
URI string `json:"uri"`
|
|
Version int `json:"version"`
|
|
}
|
|
|
|
type textDocumentIdentifier struct {
|
|
URI string `json:"uri"`
|
|
}
|
|
|
|
type contentChangeEvent struct {
|
|
Text string `json:"text"`
|
|
}
|
|
|
|
type didChangeParams struct {
|
|
TextDocument versionedTextDocumentIdentifier `json:"textDocument"`
|
|
ContentChanges []contentChangeEvent `json:"contentChanges"`
|
|
}
|
|
|
|
type didCloseParams struct {
|
|
TextDocument textDocumentIdentifier `json:"textDocument"`
|
|
}
|
|
|
|
// --- completion -------------------------------------------------------------
|
|
|
|
// Completion item kinds (a useful subset).
|
|
const (
|
|
ciFunction = 3
|
|
ciField = 5
|
|
ciVariable = 6
|
|
ciClass = 7
|
|
ciModule = 9
|
|
ciKeyword = 14
|
|
ciConstant = 21
|
|
ciStruct = 22
|
|
)
|
|
|
|
// CompletionItem is one completion suggestion.
|
|
type CompletionItem struct {
|
|
Label string `json:"label"`
|
|
Kind int `json:"kind,omitempty"`
|
|
Detail string `json:"detail,omitempty"`
|
|
Documentation string `json:"documentation,omitempty"`
|
|
InsertText string `json:"insertText,omitempty"`
|
|
}
|
|
|
|
type completionParams struct {
|
|
TextDocument textDocumentIdentifier `json:"textDocument"`
|
|
Position Position `json:"position"`
|
|
}
|
|
|
|
// --- hover ------------------------------------------------------------------
|
|
|
|
type hoverParams struct {
|
|
TextDocument textDocumentIdentifier `json:"textDocument"`
|
|
Position Position `json:"position"`
|
|
}
|
|
|
|
// Hover is the hover response.
|
|
type Hover struct {
|
|
Contents markupContent `json:"contents"`
|
|
Range Range `json:"range,omitempty"`
|
|
}
|
|
|
|
type markupContent struct {
|
|
Kind string `json:"kind"`
|
|
Value string `json:"value"`
|
|
}
|
|
|
|
// --- document symbols -------------------------------------------------------
|
|
|
|
// Symbol kinds (a useful subset).
|
|
const (
|
|
symFunction = 12
|
|
symConstant = 14
|
|
symVariable = 13
|
|
)
|
|
|
|
// DocumentSymbol is a hierarchical symbol.
|
|
type DocumentSymbol struct {
|
|
Name string `json:"name"`
|
|
Detail string `json:"detail,omitempty"`
|
|
Kind int `json:"kind"`
|
|
Range Range `json:"range"`
|
|
SelectionRange Range `json:"selectionRange"`
|
|
Children []DocumentSymbol `json:"children,omitempty"`
|
|
}
|
|
|
|
type documentSymbolParams struct {
|
|
TextDocument textDocumentIdentifier `json:"textDocument"`
|
|
}
|
|
|
|
// --- semantic tokens --------------------------------------------------------
|
|
|
|
// semanticTokenTypes is the legend of token type names, in index order. The
|
|
// indices are referenced by the encoder below.
|
|
var semanticTokenTypes = []string{
|
|
"comment", // 0
|
|
"keyword", // 1
|
|
"function", // 2
|
|
"variable", // 3
|
|
"type", // 4
|
|
"number", // 5
|
|
"string", // 6
|
|
"operator", // 7
|
|
"property", // 8
|
|
"namespace", // 9
|
|
"macro", // 10
|
|
}
|
|
|
|
const (
|
|
stComment = 0
|
|
stKeyword = 1
|
|
stFunction = 2
|
|
stVariable = 3
|
|
stType = 4
|
|
stNumber = 5
|
|
stString = 6
|
|
stOperator = 7
|
|
stProperty = 8
|
|
stNamespace = 9
|
|
stMacro = 10
|
|
)
|
|
|
|
// SemanticTokensLegend advertises the token classification.
|
|
type SemanticTokensLegend struct {
|
|
TokenTypes []string `json:"tokenTypes"`
|
|
TokenModifiers []string `json:"tokenModifiers"`
|
|
}
|
|
|
|
// SemanticTokens is the encoded token payload.
|
|
type SemanticTokens struct {
|
|
Data []int `json:"data"`
|
|
}
|
|
|
|
type semanticTokensParams struct {
|
|
TextDocument textDocumentIdentifier `json:"textDocument"`
|
|
}
|
|
|
|
// --- initialize -------------------------------------------------------------
|
|
|
|
type initializeParams struct {
|
|
RootURI string `json:"rootUri"`
|
|
}
|
|
|
|
// ServerCapabilities advertises what this server provides.
|
|
type ServerCapabilities struct {
|
|
TextDocumentSync int `json:"textDocumentSync"`
|
|
CompletionProvider map[string]any `json:"completionProvider,omitempty"`
|
|
HoverProvider bool `json:"hoverProvider,omitempty"`
|
|
DocumentSymbolProvider bool `json:"documentSymbolProvider,omitempty"`
|
|
SemanticTokensProvider map[string]any `json:"semanticTokensProvider,omitempty"`
|
|
DiagnosticProvider map[string]any `json:"diagnosticProvider,omitempty"`
|
|
}
|
|
|
|
type initializeResult struct {
|
|
Capabilities ServerCapabilities `json:"capabilities"`
|
|
ServerInfo map[string]string `json:"serverInfo,omitempty"`
|
|
}
|