Skip to content

all: support LSP 3.17 spec #52

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 19 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion .golangci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,6 @@ linters:
- staticcheck # It's a set of rules from staticcheck. It's not the same thing as the staticcheck binary. The author of staticcheck doesn't support or approve the use of staticcheck as a library inside golangci-lint.
- stylecheck # Stylecheck is a replacement for golint
- tagalign # check that struct tags are well aligned
- tagliatelle # Checks the struct tags.
- tenv # tenv is analyzer that detects using os.Setenv instead of t.Setenv since Go1.17
- testableexamples # linter checks if examples are testable (have an expected output)
- thelper # thelper detects Go test helpers without t.Helper() call and checks the consistency of test helpers
Expand Down
7 changes: 4 additions & 3 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -64,9 +64,10 @@ coverage: tools/bin/gotestsum ## Takes packages test coverage.
##@ fmt, lint

.PHONY: fmt
fmt: tools/goimportz tools/gofumpt ## Run goimportz and gofumpt.
fmt: tools/goimports-reviser tools/gofumpt ## Run goimports-reviser and gofumpt.
$(call target)
find . -iname "*.go" -not -path "./vendor/**" | xargs -P ${JOBS} ${TOOLS_BIN}/goimportz -local=${PKG},$(subst /protocol,,$(PKG)) -w
@${TOOLS_BIN}/goimports-reviser -use-cache -project-name ${PKG} -company-prefixes $(dir ${PKG}) -set-alias -excludes "vendor/*,tools/*,_*,**/.*" ./...
${TOOLS_BIN}/gofumpt -extra -w .
find . -iname "*.go" -not -path "./vendor/**" | xargs -P ${JOBS} ${TOOLS_BIN}/gofumpt -extra -w

.PHONY: lint
Expand All @@ -89,7 +90,7 @@ tools/%: ## install an individual dependent tool
tools/bin/%: ${TOOLS_DIR}/go.mod ${TOOLS_DIR}/go.sum
@cd tools; \
for t in ${TOOLS}; do \
if [ -z '$*' ] || [ $$(basename $$t) = '$*' ]; then \
if [ -z '$*' ] || [ $$(basename $${t%%/v[0-9]*}) = '$*' ]; then \
echo "Install $$t ..." >&2; \
GOBIN=${TOOLS_BIN} CGO_ENABLED=0 go install -mod=mod ${GO_FLAGS} "$${t}"; \
fi \
Expand Down
111 changes: 36 additions & 75 deletions base.go
Original file line number Diff line number Diff line change
@@ -1,96 +1,57 @@
// SPDX-FileCopyrightText: 2021 The Go Language Server Authors
// Copyright 2025 The Go Language Server Authors
// SPDX-License-Identifier: BSD-3-Clause

package protocol

import (
"fmt"
// ErrorCodes predefined error codes.
type ErrorCodes int32

"github.com/segmentio/encoding/json"
)
const (
ParseErrorErrorCodes ErrorCodes = -32700

// CancelParams params of cancelRequest.
type CancelParams struct {
// ID is the request id to cancel.
ID interface{} `json:"id"` // int32 | string
}
InvalidRequestErrorCodes ErrorCodes = -32600

// ProgressParams params of Progress netification.
//
// @since 3.15.0.
type ProgressParams struct {
// Token is the progress token provided by the client or server.
Token ProgressToken `json:"token"`
MethodNotFoundErrorCodes ErrorCodes = -32601

// Value is the progress data.
Value interface{} `json:"value"`
}
InvalidParamsErrorCodes ErrorCodes = -32602

// ProgressToken is the progress token provided by the client or server.
//
// @since 3.15.0.
type ProgressToken struct {
name string
number int32
}
InternalErrorErrorCodes ErrorCodes = -32603

// ServerNotInitializedErrorCodes error code indicating that a server received a notification or request before the server has received the `initialize` request.
ServerNotInitializedErrorCodes ErrorCodes = -32002

// compile time check whether the ProgressToken implements a fmt.Formatter, fmt.Stringer, json.Marshaler and json.Unmarshaler interfaces.
var (
_ fmt.Formatter = (*ProgressToken)(nil)
_ fmt.Stringer = (*ProgressToken)(nil)
_ json.Marshaler = (*ProgressToken)(nil)
_ json.Unmarshaler = (*ProgressToken)(nil)
UnknownErrorCodeErrorCodes ErrorCodes = -32001
)

// NewProgressToken returns a new ProgressToken.
func NewProgressToken(s string) *ProgressToken {
return &ProgressToken{name: s}
}
type LSPErrorCodes int32

// NewNumberProgressToken returns a new number ProgressToken.
func NewNumberProgressToken(n int32) *ProgressToken {
return &ProgressToken{number: n}
}
const (
// RequestFailedLSPErrorCodes a request failed but it was syntactically correct, e.g the method name was known and the parameters were valid. The error message should contain human readable information about why the request failed.
//
// @since 3.17.0
RequestFailedLSPErrorCodes LSPErrorCodes = -32803

// Format writes the ProgressToken to the formatter.
//
// If the rune is q the representation is non ambiguous,
// string forms are quoted.
func (v ProgressToken) Format(f fmt.State, r rune) {
const numF = `%d`
strF := `%s`
if r == 'q' {
strF = `%q`
}

switch {
case v.name != "":
fmt.Fprintf(f, strF, v.name)
default:
fmt.Fprintf(f, numF, v.number)
}
}
// ServerCancelledLSPErrorCodes the server cancelled the request. This error code should only be used for requests that explicitly support being server cancellable.
//
// @since 3.17.0
ServerCancelledLSPErrorCodes LSPErrorCodes = -32802

// String returns a string representation of the ProgressToken.
func (v ProgressToken) String() string {
return fmt.Sprint(v) //nolint:gocritic
}
// ContentModifiedLSPErrorCodes the server detected that the content of a document got modified outside normal conditions. A server should NOT send this error code if it detects a content change in it unprocessed messages. The result even computed on an older state might still be useful for the client. If a client decides that a result is not of any use anymore the client should cancel the request.
ContentModifiedLSPErrorCodes LSPErrorCodes = -32801

// MarshalJSON implements json.Marshaler.
func (v *ProgressToken) MarshalJSON() ([]byte, error) {
if v.name != "" {
return json.Marshal(v.name)
}
// RequestCancelledLSPErrorCodes the client has canceled a request and a server has detected the cancel.
RequestCancelledLSPErrorCodes LSPErrorCodes = -32800
)

return json.Marshal(v.number)
type CancelParams struct {
// ID the request id to cancel.
ID OneOf[int32, string] `json:"id"`
}

// UnmarshalJSON implements json.Unmarshaler.
func (v *ProgressToken) UnmarshalJSON(data []byte) error {
*v = ProgressToken{}
if err := json.Unmarshal(data, &v.number); err == nil {
return nil
}
type ProgressParams struct {
// Token the progress token provided by the client or server.
Token ProgressToken `json:"token"`

return json.Unmarshal(data, &v.name)
// Value the progress data.
Value any `json:"value"`
}
25 changes: 11 additions & 14 deletions base_test.go
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: 2021 The Go Language Server Authors
// Copyright 2021 The Go Language Server Authors
// SPDX-License-Identifier: BSD-3-Clause

package protocol
Expand All @@ -9,15 +9,14 @@ import (

"github.com/google/go-cmp/cmp"
"github.com/google/go-cmp/cmp/cmpopts"
"github.com/segmentio/encoding/json"
)

func TestCancelParams(t *testing.T) {
t.Parallel()

const want = `{"id":"testID"}`
wantType := CancelParams{
ID: "testID",
ID: NewCancelParamsID("testID"),
}

t.Run("Marshal", func(t *testing.T) {
Expand All @@ -40,11 +39,10 @@ func TestCancelParams(t *testing.T) {
}

for _, tt := range tests {
tt := tt
t.Run(tt.name, func(t *testing.T) {
t.Parallel()

got, err := json.Marshal(&tt.field)
got, err := marshal(&tt.field)
if (err != nil) != tt.wantMarshalErr {
t.Fatal(err)
}
Expand Down Expand Up @@ -76,16 +74,17 @@ func TestCancelParams(t *testing.T) {
}

for _, tt := range tests {
tt := tt
t.Run(tt.name, func(t *testing.T) {
t.Parallel()

var got CancelParams
if err := json.Unmarshal([]byte(tt.field), &got); (err != nil) != tt.wantUnmarshalErr {
if err := unmarshal([]byte(tt.field), &got); (err != nil) != tt.wantUnmarshalErr {
t.Fatal(err)
}

if diff := cmp.Diff(tt.want, got); (diff != "") != tt.wantErr {
if diff := cmp.Diff(tt.want, got,
cmpopts.EquateComparable(CancelParams{}),
); (diff != "") != tt.wantErr {
t.Errorf("%s: wantErr: %t\n(-want +got)\n%s", tt.name, tt.wantErr, diff)
}
})
Expand All @@ -101,7 +100,7 @@ func TestProgressParams(t *testing.T) {

token := NewProgressToken(wantWorkDoneToken)
wantType := ProgressParams{
Token: *token,
Token: token,
Value: "testValue",
}

Expand All @@ -125,11 +124,10 @@ func TestProgressParams(t *testing.T) {
}

for _, tt := range tests {
tt := tt
t.Run(tt.name, func(t *testing.T) {
t.Parallel()

got, err := json.Marshal(&tt.field)
got, err := marshal(&tt.field)
if (err != nil) != tt.wantMarshalErr {
t.Fatal(err)
}
Expand Down Expand Up @@ -161,12 +159,11 @@ func TestProgressParams(t *testing.T) {
}

for _, tt := range tests {
tt := tt
t.Run(tt.name, func(t *testing.T) {
t.Parallel()

var got ProgressParams
if err := json.Unmarshal([]byte(tt.field), &got); (err != nil) != tt.wantUnmarshalErr {
if err := unmarshal([]byte(tt.field), &got); (err != nil) != tt.wantUnmarshalErr {
t.Fatal(err)
}

Expand All @@ -175,7 +172,7 @@ func TestProgressParams(t *testing.T) {
}

if token := got.Token; !reflect.ValueOf(token).IsZero() {
if diff := cmp.Diff(token.String(), wantWorkDoneToken); (diff != "") != tt.wantErr {
if diff := cmp.Diff(token, wantWorkDoneToken); (diff != "") != tt.wantErr {
t.Errorf("%s: wantErr: %t\n(-want +got)\n%s", tt.name, tt.wantErr, diff)
}
}
Expand Down
Loading