Files
wehub-resource-sync a06f331eb8
CI / benchmark (push) Has been skipped
install-script / posix-syntax (push) Successful in 6m1s
CI / build-onnx (push) Failing after 6m43s
init-smoke / dry-run (push) Failing after 15m57s
security / govulncheck (push) Has been cancelled
security / trivy-fs (push) Has been cancelled
CI / test (1.26, ubuntu-latest) (push) Has been cancelled
Scorecard supply-chain security / Scorecard analysis (push) Has been cancelled
CI / test (1.26, macos-latest) (push) Has been cancelled
CI / build-windows (push) Has been cancelled
CI / lint (push) Has been cancelled
install-script / powershell-syntax (push) Has been cancelled
install-script / install (macos-14) (push) Has been cancelled
install-script / install (ubuntu-latest) (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 12:33:42 +08:00

600 lines
21 KiB
Go

package lsp
import (
"encoding/json"
"strings"
)
// LSP protocol types — minimal subset needed for semantic enrichment.
// Based on LSP 3.17.
// InitializeParams is sent as the first request to the server.
type InitializeParams struct {
ProcessID int `json:"processId"`
RootURI string `json:"rootUri"`
// WorkspaceFolders carries the primary root plus any additional
// roots for cross-package resolution. Omitted when empty so
// servers that only understand rootUri keep working.
WorkspaceFolders []WorkspaceFolder `json:"workspaceFolders,omitempty"`
Capabilities ClientCapabilities `json:"capabilities"`
// InitializationOptions carries server-specific initialization
// parameters. For jdtls this includes Maven/Gradle import settings;
// for other servers it is nil/omitted.
InitializationOptions json.RawMessage `json:"initializationOptions,omitempty"`
}
// WorkspaceFolder is one root in a multi-folder LSP workspace.
type WorkspaceFolder struct {
URI string `json:"uri"`
Name string `json:"name"`
}
// ClientCapabilities declares what the client supports.
type ClientCapabilities struct {
Workspace *WorkspaceClientCapabilities `json:"workspace,omitempty"`
TextDocument TextDocumentClientCapabilities `json:"textDocument,omitempty"`
}
// WorkspaceClientCapabilities declares workspace-level capabilities.
type WorkspaceClientCapabilities struct {
ApplyEdit bool `json:"applyEdit,omitempty"`
WorkspaceEdit *WorkspaceEditClientCapabilities `json:"workspaceEdit,omitempty"`
ExecuteCommand *ExecuteCommandCapability `json:"executeCommand,omitempty"`
WorkspaceFolders bool `json:"workspaceFolders,omitempty"`
Configuration bool `json:"configuration,omitempty"`
}
// WorkspaceEditClientCapabilities declares applyEdit support.
type WorkspaceEditClientCapabilities struct {
DocumentChanges bool `json:"documentChanges,omitempty"`
ResourceOperations []string `json:"resourceOperations,omitempty"`
}
// ExecuteCommandCapability declares workspace/executeCommand support.
type ExecuteCommandCapability struct {
DynamicRegistration bool `json:"dynamicRegistration,omitempty"`
}
// TextDocumentClientCapabilities declares text document capabilities.
type TextDocumentClientCapabilities struct {
Implementation *ImplementationCapability `json:"implementation,omitempty"`
References *ReferencesCapability `json:"references,omitempty"`
Definition *DefinitionCapability `json:"definition,omitempty"`
Hover *HoverCapability `json:"hover,omitempty"`
CallHierarchy *CallHierarchyCapability `json:"callHierarchy,omitempty"`
TypeHierarchy *TypeHierarchyCapability `json:"typeHierarchy,omitempty"`
CodeAction *CodeActionCapability `json:"codeAction,omitempty"`
PublishDiagnostics *PublishDiagnosticsCapability `json:"publishDiagnostics,omitempty"`
Synchronization *SynchronizationCapability `json:"synchronization,omitempty"`
}
// SynchronizationCapability advertises didOpen/didChange support.
type SynchronizationCapability struct {
DynamicRegistration bool `json:"dynamicRegistration,omitempty"`
WillSave bool `json:"willSave,omitempty"`
DidSave bool `json:"didSave,omitempty"`
}
// CodeActionCapability advertises codeAction request support.
type CodeActionCapability struct {
DynamicRegistration bool `json:"dynamicRegistration,omitempty"`
CodeActionLiteralSupport *CodeActionLiteralSupport `json:"codeActionLiteralSupport,omitempty"`
IsPreferredSupport bool `json:"isPreferredSupport,omitempty"`
DataSupport bool `json:"dataSupport,omitempty"`
ResolveSupport *CodeActionResolveSupport `json:"resolveSupport,omitempty"`
}
// CodeActionLiteralSupport declares supported code action kinds.
type CodeActionLiteralSupport struct {
CodeActionKind CodeActionKindCapability `json:"codeActionKind"`
}
// CodeActionKindCapability lists known code action kinds.
type CodeActionKindCapability struct {
ValueSet []string `json:"valueSet"`
}
// CodeActionResolveSupport declares which CodeAction fields the
// server can be asked to resolve lazily via codeAction/resolve.
type CodeActionResolveSupport struct {
Properties []string `json:"properties"`
}
// PublishDiagnosticsCapability advertises diagnostic-related features.
type PublishDiagnosticsCapability struct {
RelatedInformation bool `json:"relatedInformation,omitempty"`
VersionSupport bool `json:"versionSupport,omitempty"`
CodeDescriptionSupport bool `json:"codeDescriptionSupport,omitempty"`
DataSupport bool `json:"dataSupport,omitempty"`
}
// CallHierarchyCapability declares callHierarchy/* request support.
type CallHierarchyCapability struct {
DynamicRegistration bool `json:"dynamicRegistration,omitempty"`
}
// TypeHierarchyCapability declares typeHierarchy/* request support.
type TypeHierarchyCapability struct {
DynamicRegistration bool `json:"dynamicRegistration,omitempty"`
}
// ImplementationCapability declares implementation request support.
type ImplementationCapability struct {
DynamicRegistration bool `json:"dynamicRegistration,omitempty"`
}
// ReferencesCapability declares references request support.
type ReferencesCapability struct {
DynamicRegistration bool `json:"dynamicRegistration,omitempty"`
}
// DefinitionCapability declares definition request support.
type DefinitionCapability struct {
DynamicRegistration bool `json:"dynamicRegistration,omitempty"`
}
// HoverCapability declares hover request support.
type HoverCapability struct {
DynamicRegistration bool `json:"dynamicRegistration,omitempty"`
ContentFormat []string `json:"contentFormat,omitempty"`
}
// InitializeResult is the server's response to initialize.
type InitializeResult struct {
Capabilities ServerCapabilities `json:"capabilities"`
}
// ServerCapabilities declares what the server supports.
type ServerCapabilities struct {
ImplementationProvider any `json:"implementationProvider,omitempty"`
ReferencesProvider any `json:"referencesProvider,omitempty"`
DefinitionProvider any `json:"definitionProvider,omitempty"`
HoverProvider any `json:"hoverProvider,omitempty"`
CallHierarchyProvider any `json:"callHierarchyProvider,omitempty"`
TypeHierarchyProvider any `json:"typeHierarchyProvider,omitempty"`
CodeActionProvider any `json:"codeActionProvider,omitempty"`
ExecuteCommandProvider *ExecuteCommandOptions `json:"executeCommandProvider,omitempty"`
TextDocumentSync any `json:"textDocumentSync,omitempty"`
}
// ExecuteCommandOptions tells which commands the server can execute.
type ExecuteCommandOptions struct {
Commands []string `json:"commands,omitempty"`
}
// Registration is one capability the server is registering dynamically
// via client/registerCapability. The id is opaque and used as the key
// for a later client/unregisterCapability. Method names the LSP feature
// (e.g. "textDocument/foldingRange"). RegisterOptions carries the
// server's configuration for the capability (a document selector,
// trigger characters, etc.) — opaque to us; we just preserve it so
// callers that want to consult it can.
type Registration struct {
ID string `json:"id"`
Method string `json:"method"`
RegisterOptions json.RawMessage `json:"registerOptions,omitempty"`
}
// RegistrationParams is the payload of client/registerCapability.
type RegistrationParams struct {
Registrations []Registration `json:"registrations"`
}
// Unregistration identifies a previously-registered capability to drop.
// The LSP spec uses the misspelled wire field "unregisterations" — keep
// that spelling on the params struct, but the singular type is sane.
type Unregistration struct {
ID string `json:"id"`
Method string `json:"method"`
}
// UnregistrationParams is the payload of client/unregisterCapability.
// The JSON field name "unregisterations" is the LSP spec's misspelling
// — keep it; servers send that exact name on the wire.
type UnregistrationParams struct {
Unregisterations []Unregistration `json:"unregisterations"`
}
// TextDocumentIdentifier identifies a text document.
type TextDocumentIdentifier struct {
URI string `json:"uri"`
}
// TextDocumentPositionParams identifies a position in a text document.
type TextDocumentPositionParams struct {
TextDocument TextDocumentIdentifier `json:"textDocument"`
Position Position `json:"position"`
}
// Position in a text document (0-indexed).
type Position struct {
Line int `json:"line"`
Character int `json:"character"`
}
// Location represents a location in a document.
type Location struct {
URI string `json:"uri"`
Range Range `json:"range"`
}
// Range in a text document.
type Range struct {
Start Position `json:"start"`
End Position `json:"end"`
}
// DidOpenTextDocumentParams is sent when a document is opened.
type DidOpenTextDocumentParams struct {
TextDocument TextDocumentItem `json:"textDocument"`
}
// TextDocumentItem is a text document with content.
type TextDocumentItem struct {
URI string `json:"uri"`
LanguageID string `json:"languageId"`
Version int `json:"version"`
Text string `json:"text"`
}
// ReferenceParams extends TextDocumentPositionParams with reference context.
type ReferenceParams struct {
TextDocumentPositionParams
Context ReferenceContext `json:"context"`
}
// ReferenceContext controls what references are returned.
type ReferenceContext struct {
IncludeDeclaration bool `json:"includeDeclaration"`
}
// ImplementationParams is the params for textDocument/implementation.
type ImplementationParams struct {
TextDocumentPositionParams
}
// HoverParams is the params for textDocument/hover.
type HoverParams struct {
TextDocumentPositionParams
}
// HoverResult is the response for textDocument/hover.
type HoverResult struct {
Contents HoverContents `json:"contents"`
Range *Range `json:"range,omitempty"`
}
// HoverContents handles the three LSP hover content formats:
// - MarkupContent (object with kind + value)
// - MarkedString (plain string or {language, value} object)
// - MarkedString[] (array of the above)
//
// jdtls returns an array, which caused "cannot unmarshal array into Go struct"
// when Contents was typed as MarkupContent directly.
type HoverContents struct {
Value string // the concatenated hover text
}
func (hc *HoverContents) UnmarshalJSON(data []byte) error {
// Try MarkupContent first (object with kind + value).
var mc MarkupContent
if err := json.Unmarshal(data, &mc); err == nil && mc.Value != "" {
hc.Value = mc.Value
return nil
}
// Try MarkedString[] (array).
var arr []json.RawMessage
if err := json.Unmarshal(data, &arr); err == nil {
var parts []string
for _, raw := range arr {
// Each element can be a string or a MarkedString object.
var s string
if json.Unmarshal(raw, &s) == nil {
parts = append(parts, s)
continue
}
var ms MarkedString
if json.Unmarshal(raw, &ms) == nil {
parts = append(parts, ms.Value)
}
}
hc.Value = strings.Join(parts, "\n")
return nil
}
// Try plain string (MarkedString without array).
var s string
if err := json.Unmarshal(data, &s); err == nil {
hc.Value = s
return nil
}
// Fallback: single MarkedString object.
var ms MarkedString
if err := json.Unmarshal(data, &ms); err == nil {
hc.Value = ms.Value
return nil
}
return nil // empty/unrecognised — leave Value empty
}
// MarkedString represents a {language, value} object in hover content.
type MarkedString struct {
Language string `json:"language"`
Value string `json:"value"`
}
// MarkupContent represents hover content.
type MarkupContent struct {
Kind string `json:"kind"`
Value string `json:"value"`
}
// CallHierarchyPrepareParams is the params for callHierarchy/prepare.
type CallHierarchyPrepareParams struct {
TextDocumentPositionParams
}
// CallHierarchyItem represents an item in the call hierarchy.
type CallHierarchyItem struct {
Name string `json:"name"`
Kind int `json:"kind"`
URI string `json:"uri"`
Range Range `json:"range"`
SelectionRange Range `json:"selectionRange"`
}
// CallHierarchyIncomingCallsParams is the params for callHierarchy/incomingCalls.
type CallHierarchyIncomingCallsParams struct {
Item CallHierarchyItem `json:"item"`
}
// CallHierarchyIncomingCall represents an incoming call.
type CallHierarchyIncomingCall struct {
From CallHierarchyItem `json:"from"`
FromRanges []Range `json:"fromRanges"`
}
// CallHierarchyOutgoingCallsParams is the params for callHierarchy/outgoingCalls.
type CallHierarchyOutgoingCallsParams struct {
Item CallHierarchyItem `json:"item"`
}
// CallHierarchyOutgoingCall represents an outgoing call.
type CallHierarchyOutgoingCall struct {
To CallHierarchyItem `json:"to"`
FromRanges []Range `json:"fromRanges"`
}
// TypeHierarchyPrepareParams is the params for textDocument/prepareTypeHierarchy.
type TypeHierarchyPrepareParams struct {
TextDocumentPositionParams
}
// TypeHierarchyItem represents an item in the type hierarchy.
type TypeHierarchyItem struct {
Name string `json:"name"`
Kind int `json:"kind"`
URI string `json:"uri"`
Range Range `json:"range"`
SelectionRange Range `json:"selectionRange"`
}
// TypeHierarchySupertypesParams is the params for typeHierarchy/supertypes.
type TypeHierarchySupertypesParams struct {
Item TypeHierarchyItem `json:"item"`
}
// TypeHierarchySubtypesParams is the params for typeHierarchy/subtypes.
type TypeHierarchySubtypesParams struct {
Item TypeHierarchyItem `json:"item"`
}
// ---------------------------------------------------------------------------
// textDocument/codeAction support (LSP fix-all).
// ---------------------------------------------------------------------------
// Standard LSP code-action kinds (a subset).
const (
CodeActionKindEmpty = ""
CodeActionKindQuickFix = "quickfix"
CodeActionKindRefactor = "refactor"
CodeActionKindRefactorExtract = "refactor.extract"
CodeActionKindRefactorInline = "refactor.inline"
CodeActionKindRefactorRewrite = "refactor.rewrite"
CodeActionKindSource = "source"
CodeActionKindSourceOrganizeImports = "source.organizeImports"
CodeActionKindSourceFixAll = "source.fixAll"
)
// DiagnosticSeverity values match the LSP spec.
const (
DiagSeverityError = 1
DiagSeverityWarning = 2
DiagSeverityInformation = 3
DiagSeverityHint = 4
)
// Diagnostic is a problem report from the server.
type Diagnostic struct {
Range Range `json:"range"`
Severity int `json:"severity,omitempty"`
Code any `json:"code,omitempty"`
CodeDescription *DiagnosticCodeDescription `json:"codeDescription,omitempty"`
Source string `json:"source,omitempty"`
Message string `json:"message"`
Tags []int `json:"tags,omitempty"`
RelatedInformation []DiagnosticRelatedInformation `json:"relatedInformation,omitempty"`
Data any `json:"data,omitempty"`
}
// DiagnosticCodeDescription points to documentation for the code.
type DiagnosticCodeDescription struct {
Href string `json:"href"`
}
// DiagnosticRelatedInformation locates a related document position.
type DiagnosticRelatedInformation struct {
Location Location `json:"location"`
Message string `json:"message"`
}
// PublishDiagnosticsParams is the payload of textDocument/publishDiagnostics.
type PublishDiagnosticsParams struct {
URI string `json:"uri"`
Version *int `json:"version,omitempty"`
Diagnostics []Diagnostic `json:"diagnostics"`
}
// CodeActionParams is the params for textDocument/codeAction.
type CodeActionParams struct {
TextDocument TextDocumentIdentifier `json:"textDocument"`
Range Range `json:"range"`
Context CodeActionContext `json:"context"`
}
// CodeActionContext narrows the kinds of actions returned and lists
// diagnostics those actions should address.
type CodeActionContext struct {
Diagnostics []Diagnostic `json:"diagnostics"`
Only []string `json:"only,omitempty"`
TriggerKind int `json:"triggerKind,omitempty"`
}
// Command is a server-defined command (e.g. "go.organizeImports").
type Command struct {
Title string `json:"title"`
Command string `json:"command"`
Arguments []any `json:"arguments,omitempty"`
}
// CodeAction is the rich form returned from textDocument/codeAction.
// Either Edit or Command (or both) may be set; servers vary.
type CodeAction struct {
Title string `json:"title"`
Kind string `json:"kind,omitempty"`
Diagnostics []Diagnostic `json:"diagnostics,omitempty"`
IsPreferred bool `json:"isPreferred,omitempty"`
Disabled *DisabledReason `json:"disabled,omitempty"`
Edit *WorkspaceEdit `json:"edit,omitempty"`
Command *Command `json:"command,omitempty"`
Data any `json:"data,omitempty"`
}
// DisabledReason explains why a server marked a code action disabled.
type DisabledReason struct {
Reason string `json:"reason"`
}
// CodeActionOrCommand is the union returned by textDocument/codeAction.
// LSP servers may emit either a Command (legacy) or a CodeAction
// literal — we accept both and normalise downstream.
type CodeActionOrCommand struct {
// Title is always present and human readable.
Title string `json:"title,omitempty"`
// Command is set on legacy (Command) form.
Command string `json:"command,omitempty"`
// Arguments accompanies legacy Command form.
Arguments []any `json:"arguments,omitempty"`
// Kind / Edit / Diagnostics / IsPreferred / Disabled are part of
// the CodeAction literal form.
Kind string `json:"kind,omitempty"`
Edit *WorkspaceEdit `json:"edit,omitempty"`
Diagnostics []Diagnostic `json:"diagnostics,omitempty"`
IsPreferred bool `json:"isPreferred,omitempty"`
Disabled *DisabledReason `json:"disabled,omitempty"`
Data any `json:"data,omitempty"`
}
// IsCommand reports whether the action is a legacy Command (no edit).
func (a *CodeActionOrCommand) IsCommand() bool {
return a != nil && a.Command != "" && a.Edit == nil
}
// AsCodeAction projects the union into the literal CodeAction form.
func (a *CodeActionOrCommand) AsCodeAction() *CodeAction {
if a == nil {
return nil
}
if a.Edit == nil && a.Command == "" {
return &CodeAction{Title: a.Title, Kind: a.Kind, IsPreferred: a.IsPreferred, Diagnostics: a.Diagnostics, Disabled: a.Disabled, Data: a.Data}
}
out := &CodeAction{
Title: a.Title,
Kind: a.Kind,
Edit: a.Edit,
Diagnostics: a.Diagnostics,
IsPreferred: a.IsPreferred,
Disabled: a.Disabled,
Data: a.Data,
}
if a.Command != "" {
out.Command = &Command{Title: a.Title, Command: a.Command, Arguments: a.Arguments}
}
return out
}
// WorkspaceEdit groups edits across multiple documents.
type WorkspaceEdit struct {
Changes map[string][]TextEdit `json:"changes,omitempty"`
DocumentChanges []TextDocumentEdit `json:"documentChanges,omitempty"`
}
// TextDocumentEdit is the per-document grouping in a WorkspaceEdit.
type TextDocumentEdit struct {
TextDocument VersionedTextDocumentIdentifier `json:"textDocument"`
Edits []TextEdit `json:"edits"`
}
// VersionedTextDocumentIdentifier carries a version for optimistic edits.
type VersionedTextDocumentIdentifier struct {
URI string `json:"uri"`
Version int `json:"version"`
}
// TextEdit is one textual change in a document.
type TextEdit struct {
Range Range `json:"range"`
NewText string `json:"newText"`
}
// ApplyWorkspaceEditParams is the params of workspace/applyEdit.
type ApplyWorkspaceEditParams struct {
Label string `json:"label,omitempty"`
Edit WorkspaceEdit `json:"edit"`
}
// ApplyWorkspaceEditResponse is the server's reply (we send these as a
// reverse request — the server sends them to the client when it wants
// to apply edits via a command).
type ApplyWorkspaceEditResponse struct {
Applied bool `json:"applied"`
FailureReason string `json:"failureReason,omitempty"`
}
// ExecuteCommandParams is the params of workspace/executeCommand.
type ExecuteCommandParams struct {
Command string `json:"command"`
Arguments []any `json:"arguments,omitempty"`
}
// DidChangeTextDocumentParams is sent when a document version moves.
type DidChangeTextDocumentParams struct {
TextDocument VersionedTextDocumentIdentifier `json:"textDocument"`
ContentChanges []TextDocumentContentChangeEvent `json:"contentChanges"`
}
// TextDocumentContentChangeEvent is a single edit to a document.
// When Range is nil the change replaces the whole text.
type TextDocumentContentChangeEvent struct {
Range *Range `json:"range,omitempty"`
Text string `json:"text"`
}
// DidCloseTextDocumentParams is sent on textDocument/didClose.
type DidCloseTextDocumentParams struct {
TextDocument TextDocumentIdentifier `json:"textDocument"`
}