diff --git a/lsp/protocol_test.go b/lsp/protocol_test.go index 4986bdf..fc213ce 100644 --- a/lsp/protocol_test.go +++ b/lsp/protocol_test.go @@ -64,3 +64,49 @@ func TestDocumentSymbolParse(t *testing.T) { require.Equal(t, "10:5-10:9", symbols[2].SelectionRange.String()) fmt.Printf("%+v\n", res) } + +func TestVariousMessages(t *testing.T) { + msg := `{ + "capabilities":{ + "codeActionProvider":{ + "codeActionKinds":["quickfix","refactor","info"]}, + "completionProvider":{ + "allCommitCharacters":[" ","\t","(",")","[","]","{","}","<",">",":",";",",","+","-","/","*","%","^","&","#","?",".","=","\"","'","|"], + "resolveProvider":false, + "triggerCharacters":[".","<",">",":","\"","/"]}, + "declarationProvider":true, + "definitionProvider":true, + "documentFormattingProvider":true, + "documentHighlightProvider":true, + "documentLinkProvider":{"resolveProvider":false}, + "documentOnTypeFormattingProvider":{ + "firstTriggerCharacter":"\n", + "moreTriggerCharacter":[]}, + "documentRangeFormattingProvider":true, + "documentSymbolProvider":true, + "executeCommandProvider":{"commands":["clangd.applyFix","clangd.applyTweak"]}, + "hoverProvider":true, + "referencesProvider":true, + "renameProvider":{"prepareProvider":true}, + "selectionRangeProvider":true, + "semanticTokensProvider":{ + "full":{"delta":true}, + "legend":{ + "tokenModifiers":[], + "tokenTypes":["variable","variable","parameter","function","member","function","member","variable","class","enum","enumConstant","type","dependent","dependent","namespace","typeParameter","concept","type","macro","comment"] + }, + "range":false}, + "signatureHelpProvider":{"triggerCharacters":["(",","]}, + "textDocumentSync":{ + "change":2, + "openClose":true, + "save":true + }, + "typeHierarchyProvider":true, + "workspaceSymbolProvider":true + }, + "serverInfo":{"name":"clangd","version":"clangd version 11.0.0 (https://github.com/llvm/llvm-project 176249bd6732a8044d457092ed932768724a6f06)"}}` + var init InitializeResult + err := json.Unmarshal([]byte(msg), &init) + require.NoError(t, err) +} diff --git a/lsp/service.go b/lsp/service.go index b08d5a4..06d91c3 100644 --- a/lsp/service.go +++ b/lsp/service.go @@ -244,7 +244,45 @@ type TextDocumentSyncOptions struct { Change TextDocumentSyncKind `json:"change"` WillSave bool `json:"willSave,omitempty"` WillSaveWaitUntil bool `json:"willSaveWaitUntil,omitempty"` - Save *SaveOptions `json:"save,omitempty"` + Save *BoolOrSaveOptions `json:"save,omitempty"` +} + +type BoolOrSaveOptions struct { + Save *bool + SaveOptions *SaveOptions +} + +// MarshalJSON implements json.Marshaler. +func (v *BoolOrSaveOptions) MarshalJSON() ([]byte, error) { + if v.Save != nil { + return json.Marshal(v.Save) + } + if v.SaveOptions != nil { + return json.Marshal(v.SaveOptions) + } + return []byte("null"), nil +} + +// UnmarshalJSON implements json.Unmarshaler. +func (v *BoolOrSaveOptions) UnmarshalJSON(data []byte) error { + if bytes.Equal(data, []byte("null")) { + v.Save = nil + v.SaveOptions = nil + return nil + } + var save bool + if err := json.Unmarshal(data, &save); err == nil { + v.Save = &save + v.SaveOptions = nil + return nil + } + var saveOpts SaveOptions + if err := json.Unmarshal(data, &saveOpts); err != nil { + return err + } + v.Save = nil + v.SaveOptions = &saveOpts + return nil } // TextDocumentSyncOptions holds either a TextDocumentSyncKind or @@ -309,12 +347,12 @@ type ServerCapabilities struct { DocumentSymbolProvider bool `json:"documentSymbolProvider,omitempty"` WorkspaceSymbolProvider bool `json:"workspaceSymbolProvider,omitempty"` ImplementationProvider bool `json:"implementationProvider,omitempty"` - CodeActionProvider bool `json:"codeActionProvider,omitempty"` + CodeActionProvider *BoolOrCodeActionOptions `json:"codeActionProvider,omitempty"` CodeLensProvider *CodeLensOptions `json:"codeLensProvider,omitempty"` DocumentFormattingProvider bool `json:"documentFormattingProvider,omitempty"` DocumentRangeFormattingProvider bool `json:"documentRangeFormattingProvider,omitempty"` DocumentOnTypeFormattingProvider *DocumentOnTypeFormattingOptions `json:"documentOnTypeFormattingProvider,omitempty"` - RenameProvider bool `json:"renameProvider,omitempty"` + RenameProvider *BoolOrRenameOptions `json:"renameProvider,omitempty"` ExecuteCommandProvider *ExecuteCommandOptions `json:"executeCommandProvider,omitempty"` SemanticHighlighting *SemanticHighlightingOptions `json:"semanticHighlighting,omitempty"` @@ -352,6 +390,49 @@ type SignatureHelpOptions struct { TriggerCharacters []string `json:"triggerCharacters,omitempty"` } +type CodeActionOptions struct { + CodeActionKinds *[]CodeActionKind `json:"codeActionKinds,omitempty` + ResolveProvider *bool `json:"resolveProvider,omitempty` +} + +type BoolOrCodeActionOptions struct { + IsProvider *bool + Options *CodeActionOptions +} + +// MarshalJSON implements json.Marshaler. +func (v *BoolOrCodeActionOptions) MarshalJSON() ([]byte, error) { + if v.IsProvider != nil { + return json.Marshal(v.IsProvider) + } + if v.Options != nil { + return json.Marshal(v.Options) + } + return []byte("null"), nil +} + +// UnmarshalJSON implements json.Unmarshaler. +func (v *BoolOrCodeActionOptions) UnmarshalJSON(data []byte) error { + if bytes.Equal(data, []byte("null")) { + v.IsProvider = nil + v.Options = nil + return nil + } + var b bool + if err := json.Unmarshal(data, &b); err == nil { + v.IsProvider = &b + v.Options = nil + return nil + } + var opts CodeActionOptions + if err := json.Unmarshal(data, &opts); err != nil { + return err + } + v.IsProvider = nil + v.Options = &opts + return nil +} + type ExecuteCommandOptions struct { Commands []string `json:"commands"` } @@ -361,6 +442,48 @@ type ExecuteCommandParams struct { Arguments []interface{} `json:"arguments,omitempty"` } +type RenameOptions struct { + PrepareProvider *bool `json:"prepareProvider,omitempty` +} + +type BoolOrRenameOptions struct { + IsProvider *bool + Options *RenameOptions +} + +// MarshalJSON implements json.Marshaler. +func (v *BoolOrRenameOptions) MarshalJSON() ([]byte, error) { + if v.IsProvider != nil { + return json.Marshal(v.IsProvider) + } + if v.Options != nil { + return json.Marshal(v.Options) + } + return []byte("null"), nil +} + +// UnmarshalJSON implements json.Unmarshaler. +func (v *BoolOrRenameOptions) UnmarshalJSON(data []byte) error { + if bytes.Equal(data, []byte("null")) { + v.IsProvider = nil + v.Options = nil + return nil + } + var b bool + if err := json.Unmarshal(data, &b); err == nil { + v.IsProvider = &b + v.Options = nil + return nil + } + var opts RenameOptions + if err := json.Unmarshal(data, &opts); err != nil { + return err + } + v.IsProvider = nil + v.Options = &opts + return nil +} + type SemanticHighlightingOptions struct { Scopes [][]string `json:"scopes,omitempty"` }