430 lines
12 KiB
Go
430 lines
12 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"`
|
|
}
|
|
|
|
const (
|
|
errParse = -32700
|
|
errMethodNotFound = -32601
|
|
errInternalError = -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 -------------------------------------------------------------
|
|
|
|
const (
|
|
ciFunction = 3
|
|
|
|
ciVariable = 6
|
|
ciClass = 7
|
|
ciModule = 9
|
|
ciKeyword = 14
|
|
ciConstant = 21
|
|
)
|
|
|
|
// 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"`
|
|
}
|
|
|
|
type definitionParams struct {
|
|
TextDocument textDocumentIdentifier `json:"textDocument"`
|
|
Position Position `json:"position"`
|
|
}
|
|
|
|
type referenceParams struct {
|
|
TextDocument textDocumentIdentifier `json:"textDocument"`
|
|
Position Position `json:"position"`
|
|
Context referenceContext `json:"context"`
|
|
}
|
|
|
|
type referenceContext struct {
|
|
IncludeDeclaration bool `json:"includeDeclaration"`
|
|
}
|
|
|
|
type renameParams struct {
|
|
TextDocument textDocumentIdentifier `json:"textDocument"`
|
|
Position Position `json:"position"`
|
|
NewName string `json:"newName"`
|
|
}
|
|
|
|
// WorkspaceEdit holds changes across multiple documents.
|
|
type WorkspaceEdit struct {
|
|
Changes map[string][]TextEdit `json:"changes"`
|
|
}
|
|
|
|
// TextEdit replaces a range with new text.
|
|
type TextEdit struct {
|
|
Range Range `json:"range"`
|
|
NewText string `json:"newText"`
|
|
}
|
|
|
|
type documentFormattingParams struct {
|
|
TextDocument textDocumentIdentifier `json:"textDocument"`
|
|
Options formattingOptions `json:"options"`
|
|
}
|
|
|
|
type formattingOptions struct {
|
|
TabSize int `json:"tabSize"`
|
|
InsertSpaces bool `json:"insertSpaces"`
|
|
}
|
|
|
|
type inlayHintParams struct {
|
|
TextDocument textDocumentIdentifier `json:"textDocument"`
|
|
Range Range `json:"range"`
|
|
}
|
|
|
|
// InlayHint is a hint displayed inline in the editor.
|
|
type InlayHint struct {
|
|
Position Position `json:"position"`
|
|
Label string `json:"label"`
|
|
Kind int `json:"kind,omitempty"`
|
|
Padding bool `json:"paddingLeft,omitempty"`
|
|
Tooltip string `json:"tooltip,omitempty"`
|
|
}
|
|
|
|
const (
|
|
inlayHintTypeParameter = 2
|
|
)
|
|
|
|
// Hover is the hover response.
|
|
type Hover struct {
|
|
Contents markupContent `json:"contents"`
|
|
Range Range `json:"range"`
|
|
}
|
|
|
|
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 -------------------------------------------------------------
|
|
|
|
// 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"`
|
|
DefinitionProvider bool `json:"definitionProvider,omitempty"`
|
|
ReferencesProvider bool `json:"referencesProvider,omitempty"`
|
|
RenameProvider bool `json:"renameProvider,omitempty"`
|
|
DocumentSymbolProvider bool `json:"documentSymbolProvider,omitempty"`
|
|
DocumentFormattingProvider bool `json:"documentFormattingProvider,omitempty"`
|
|
DocumentHighlightProvider bool `json:"documentHighlightProvider,omitempty"`
|
|
CodeActionProvider bool `json:"codeActionProvider,omitempty"`
|
|
SignatureHelpProvider map[string]any `json:"signatureHelpProvider,omitempty"`
|
|
WorkspaceSymbolProvider bool `json:"workspaceSymbolProvider,omitempty"`
|
|
SemanticTokensProvider map[string]any `json:"semanticTokensProvider,omitempty"`
|
|
InlayHintProvider bool `json:"inlayHintProvider,omitempty"`
|
|
DiagnosticProvider map[string]any `json:"diagnosticProvider,omitempty"`
|
|
DocumentLinkProvider map[string]any `json:"documentLinkProvider,omitempty"`
|
|
FoldingRangeProvider bool `json:"foldingRangeProvider,omitempty"`
|
|
}
|
|
|
|
// --- pull diagnostics ---------------------------------------------------------
|
|
|
|
type diagnosticParams struct {
|
|
TextDocument textDocumentIdentifier `json:"textDocument"`
|
|
}
|
|
|
|
// documentDiagnosticReport is the pull-diagnostics result: a full report for
|
|
// one document.
|
|
type documentDiagnosticReport struct {
|
|
Kind string `json:"kind"` // always "full"
|
|
Items []Diagnostic `json:"items"`
|
|
}
|
|
|
|
// --- document links -----------------------------------------------------------
|
|
|
|
type documentLinkParams struct {
|
|
TextDocument textDocumentIdentifier `json:"textDocument"`
|
|
}
|
|
|
|
// DocumentLink links a #include path to the header file it resolves to.
|
|
type DocumentLink struct {
|
|
Range Range `json:"range"`
|
|
Target string `json:"target"`
|
|
}
|
|
|
|
// --- folding ranges -----------------------------------------------------------
|
|
|
|
type foldingRangeParams struct {
|
|
TextDocument textDocumentIdentifier `json:"textDocument"`
|
|
}
|
|
|
|
// FoldingRange marks a collapsible region: a TEXT function's body.
|
|
type FoldingRange struct {
|
|
StartLine int `json:"startLine"`
|
|
EndLine int `json:"endLine"`
|
|
Kind string `json:"kind,omitempty"`
|
|
}
|
|
|
|
// --- code actions -------------------------------------------------------------
|
|
|
|
type codeActionParams struct {
|
|
TextDocument textDocumentIdentifier `json:"textDocument"`
|
|
Range Range `json:"range"`
|
|
Context codeActionContext `json:"context"`
|
|
}
|
|
|
|
type codeActionContext struct {
|
|
Diagnostics []Diagnostic `json:"diagnostics"`
|
|
}
|
|
|
|
// CodeAction represents a quick fix or refactoring.
|
|
type CodeAction struct {
|
|
Title string `json:"title"`
|
|
Kind string `json:"kind,omitempty"`
|
|
Edit *WorkspaceEdit `json:"edit,omitempty"`
|
|
}
|
|
|
|
// --- signature help -----------------------------------------------------------
|
|
|
|
type signatureHelpParams struct {
|
|
TextDocument textDocumentIdentifier `json:"textDocument"`
|
|
Position Position `json:"position"`
|
|
}
|
|
|
|
// SignatureHelp represents signature information for a function call.
|
|
type SignatureHelp struct {
|
|
Signatures []SignatureInformation `json:"signatures"`
|
|
}
|
|
|
|
// SignatureInformation represents a function signature.
|
|
type SignatureInformation struct {
|
|
Label string `json:"label"`
|
|
Documentation string `json:"documentation,omitempty"`
|
|
Parameters []ParameterInformation `json:"parameters,omitempty"`
|
|
}
|
|
|
|
// ParameterInformation represents a parameter in a signature.
|
|
type ParameterInformation struct {
|
|
Label string `json:"label"`
|
|
}
|
|
|
|
// --- document highlights ------------------------------------------------------
|
|
|
|
type documentHighlightParams struct {
|
|
TextDocument textDocumentIdentifier `json:"textDocument"`
|
|
Position Position `json:"position"`
|
|
}
|
|
|
|
// DocumentHighlight represents a highlight range.
|
|
type DocumentHighlight struct {
|
|
Range Range `json:"range"`
|
|
Kind int `json:"kind,omitempty"`
|
|
}
|
|
|
|
const (
|
|
highlightRead = 1
|
|
highlightWrite = 2
|
|
)
|
|
|
|
// --- workspace symbols -------------------------------------------------------
|
|
|
|
type workspaceSymbolParams struct {
|
|
Query string `json:"query"`
|
|
}
|
|
|
|
// WorkspaceSymbol represents a symbol found across the workspace.
|
|
type WorkspaceSymbol struct {
|
|
Name string `json:"name"`
|
|
Kind int `json:"kind"`
|
|
Location Location `json:"location"`
|
|
}
|
|
|
|
type initializeResult struct {
|
|
Capabilities ServerCapabilities `json:"capabilities"`
|
|
ServerInfo map[string]string `json:"serverInfo,omitempty"`
|
|
}
|