diff --git a/gopls/go.mod b/gopls/go.mod index 866fb259581..2c27a14a9c3 100644 --- a/gopls/go.mod +++ b/gopls/go.mod @@ -20,9 +20,12 @@ require ( mvdan.cc/xurls/v2 v2.4.0 ) +require github.com/qiniu/x v1.11.9 // indirect + require ( github.com/BurntSushi/toml v1.2.1 // indirect github.com/google/safehtml v0.1.0 // indirect + github.com/goplus/gop v1.1.7 golang.org/x/exp/typeparams v0.0.0-20221212164502-fae10dda9338 // indirect ) diff --git a/gopls/go.sum b/gopls/go.sum index 365fd0c1a4f..fba0f15232a 100644 --- a/gopls/go.sum +++ b/gopls/go.sum @@ -6,46 +6,71 @@ github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSs github.com/frankban/quicktest v1.14.3 h1:FJKSZTDHjyhriyC81FLQ0LY93eSai0ZyR/ZIkd3ZUKE= github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/safehtml v0.0.2/go.mod h1:L4KWwDsUJdECRAEpZoBn3O64bQaywRscowZjJAzjHnU= github.com/google/safehtml v0.1.0 h1:EwLKo8qawTKfsi0orxcQAZzu07cICaBeFMegAU9eaT8= github.com/google/safehtml v0.1.0/go.mod h1:L4KWwDsUJdECRAEpZoBn3O64bQaywRscowZjJAzjHnU= +github.com/goplus/c2go v0.7.10/go.mod h1:ryOieVZqh0jmlPp45AMh9/sXIreTnyjDREP0EMX+pF0= +github.com/goplus/c2go v0.7.13/go.mod h1:s5NULWzUKi3erd3l4ahvpq+3qDGOvMjGb00a5o3h6zk= +github.com/goplus/gop v1.1.7 h1:6PemF6HYQZ2xjqIA06tQXk9LSmWz5p8+wo4UpQKTkes= +github.com/goplus/gop v1.1.7/go.mod h1:+kYk2ZSZ+rPD8AVoWTz3r5L/dkbtmUNvJOP0h1ET8KI= +github.com/goplus/gox v1.11.21/go.mod h1:wRCRSNukie4cDqADF4w0Btc2Gk6V3p3V6hI5+rsVqa8= +github.com/goplus/gox v1.11.32/go.mod h1:hdKq5ghywtKWnBJNQNVBkPITmWCqCFRwwd2LTYTfg2U= +github.com/goplus/gox v1.11.37/go.mod h1:NkgUJWIjKxrhUwM4bgyUt3ZE0WlTunqfksMzrbnh7V8= +github.com/goplus/libc v0.3.13/go.mod h1:xqG4/g3ilKBE/UDn5vkaE7RRQPQPyspj7ecuMuvlQJ8= +github.com/goplus/mod v0.9.12/go.mod h1:YoPIowz71rnLLROA4YG0AC8bzDtPRyMaQwgTRLr8ri4= +github.com/goplus/mod v0.11.2/go.mod h1:d40I3nOr2qkGfLUjwc/BqLSq3oUUlQy5+/SpdiBKgY4= github.com/jba/printsrc v0.2.2 h1:9OHK51UT+/iMAEBlQIIXW04qvKyF3/vvLuwW/hL8tDU= github.com/jba/printsrc v0.2.2/go.mod h1:1xULjw59sL0dPdWpDoVU06TIEO/Wnfv6AHRpiElTwYM= github.com/jba/templatecheck v0.6.0 h1:SwM8C4hlK/YNLsdcXStfnHWE2HKkuTVwy5FKQHt5ro8= github.com/jba/templatecheck v0.6.0/go.mod h1:/1k7EajoSErFI9GLHAsiIJEaNLt3ALKNw2TV7z2SYv4= +github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= +github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= +github.com/petermattis/goid v0.0.0-20220331194723-8ee3e6ded87a/go.mod h1:pxMtw7cyUw6B2bRH0ZBANSPg+AoSud1I1iyJHI69jH4= github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/qiniu/x v1.11.9 h1:IfQNdeNcK43Q1+b/LdrcqmWjlhxq051YVBnua8J2qN8= +github.com/qiniu/x v1.11.9/go.mod h1:03Ni9tj+N2h2aKnAz+6N0Xfl8FwMEDRC2PAlxekASDs= github.com/rogpeppe/go-internal v1.8.1/go.mod h1:JeRgkft04UBgHMgCIwADu4Pn6Mtm5d4nPKWu0nJ5d+o= github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8= github.com/sergi/go-diff v1.1.0 h1:we8PVUC3FE2uYfodKH/nBHMSetSfHDR6scGdBi+erh0= github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= +golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4= golang.org/x/exp/typeparams v0.0.0-20221212164502-fae10dda9338 h1:2O2DON6y3XMJiQRAS1UWU+54aec2uopH3x7MAiqGW6Y= golang.org/x/exp/typeparams v0.0.0-20221212164502-fae10dda9338/go.mod h1:AbB0pIl9nAr9wVwH+Z2ZpaocVmF5I4GyWCDIsVjR0bk= +golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= +golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/mod v0.13.0 h1:I/DsJXRlw/8l/0c24sM9yb0T4z9liZTduXvdAWYiysY= golang.org/x/mod v0.13.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= +golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= golang.org/x/net v0.16.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.4.0 h1:zxkM55ReGkDlKSM+Fu41A+zmbZuaPVbGMzvvdUPznYQ= golang.org/x/sync v0.4.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y= +golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.13.0 h1:Af8nKPmuFypiUBjVoU9V20FiaFXOcuZI21p0ycVYYGE= golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/telemetry v0.0.0-20231003223302-0168ef4ebbd3 h1:vxxQvncMbcRAtqHV5HsHGJkbya+BIOYIY+y6cdPZhzk= golang.org/x/telemetry v0.0.0-20231003223302-0168ef4ebbd3/go.mod h1:ppZ76JTkRgJC2GQEgtVY3fiuJR+N8FU2MAlp+gfN1E4= +golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo= golang.org/x/term v0.13.0/go.mod h1:LTmsnFJwVN6bCy1rVCoS+qHT1HhALEFxKncY3WNNh4U= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= diff --git a/gopls/internal/goxls/goputil/goputil.go b/gopls/internal/goxls/goputil/goputil.go new file mode 100644 index 00000000000..a6d3a0d6275 --- /dev/null +++ b/gopls/internal/goxls/goputil/goputil.go @@ -0,0 +1,23 @@ +// Copyright 2023 The GoPlus Authors (goplus.org). All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package goputil + +type Kind int + +const ( + FileUnknown Kind = iota + FileGopNormal + FileGopClass +) + +func FileKind(fext string) Kind { + switch fext { + case ".gop": + return FileGopNormal + case ".spx", ".rdx", ".gox", ".gmx": + return FileGopClass + } + return FileUnknown +} diff --git a/gopls/internal/lsp/cache/snapshot_gox.go b/gopls/internal/lsp/cache/snapshot_gox.go index 0cc7a0d75f5..40832962688 100644 --- a/gopls/internal/lsp/cache/snapshot_gox.go +++ b/gopls/internal/lsp/cache/snapshot_gox.go @@ -5,13 +5,10 @@ package cache import ( + "golang.org/x/tools/gopls/internal/goxls/goputil" "golang.org/x/tools/gopls/internal/lsp/source" ) func checkGopFile(fh source.FileHandle, fext string) bool { - switch fext { - case ".gop", ".spx", ".rdx", ".gox", ".gmx": - return true - } - return false + return goputil.FileKind(fext) != goputil.FileUnknown } diff --git a/gopls/internal/lsp/code_action.go b/gopls/internal/lsp/code_action.go index 555131ea796..86f795e4e09 100644 --- a/gopls/internal/lsp/code_action.go +++ b/gopls/internal/lsp/code_action.go @@ -109,7 +109,7 @@ func (s *Server) codeAction(ctx context.Context, params *protocol.CodeActionPara return actions, nil - case source.Go: + case source.Go, source.Gop: // goxls: Go+ diagnostics := params.Context.Diagnostics // Don't suggest fixes for generated files, since they are generally diff --git a/gopls/internal/lsp/code_action_gox.go b/gopls/internal/lsp/code_action_gox.go new file mode 100644 index 00000000000..e598d3b34fc --- /dev/null +++ b/gopls/internal/lsp/code_action_gox.go @@ -0,0 +1,5 @@ +// Copyright 2023 The GoPlus Authors (goplus.org). All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package lsp diff --git a/gopls/internal/lsp/code_lens.go b/gopls/internal/lsp/code_lens.go index da7598604b0..afb6f7ec2bc 100644 --- a/gopls/internal/lsp/code_lens.go +++ b/gopls/internal/lsp/code_lens.go @@ -30,7 +30,7 @@ func (s *Server) codeLens(ctx context.Context, params *protocol.CodeLensParams) switch snapshot.FileKind(fh) { case source.Mod: lenses = mod.LensFuncs() - case source.Go: + case source.Go, source.Gop: // goxls: Go+ lenses = source.LensFuncs() default: // Unsupported file kind for a code lens. diff --git a/gopls/internal/lsp/code_lens_gox.go b/gopls/internal/lsp/code_lens_gox.go new file mode 100644 index 00000000000..e598d3b34fc --- /dev/null +++ b/gopls/internal/lsp/code_lens_gox.go @@ -0,0 +1,5 @@ +// Copyright 2023 The GoPlus Authors (goplus.org). All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package lsp diff --git a/gopls/internal/lsp/completion.go b/gopls/internal/lsp/completion.go index 66b3a3945bf..cb827c232f5 100644 --- a/gopls/internal/lsp/completion.go +++ b/gopls/internal/lsp/completion.go @@ -36,7 +36,7 @@ func (s *Server) completion(ctx context.Context, params *protocol.CompletionPara var candidates []completion.CompletionItem var surrounding *completion.Selection switch snapshot.FileKind(fh) { - case source.Go: + case source.Go, source.Gop: // goxls: Go+ candidates, surrounding, err = completion.Completion(ctx, snapshot, fh, params.Position, params.Context) case source.Mod: candidates, surrounding = nil, nil diff --git a/gopls/internal/lsp/completion_gox.go b/gopls/internal/lsp/completion_gox.go new file mode 100644 index 00000000000..e598d3b34fc --- /dev/null +++ b/gopls/internal/lsp/completion_gox.go @@ -0,0 +1,5 @@ +// Copyright 2023 The GoPlus Authors (goplus.org). All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package lsp diff --git a/gopls/internal/lsp/definition.go b/gopls/internal/lsp/definition.go index 892e48d6377..0a216c563f0 100644 --- a/gopls/internal/lsp/definition.go +++ b/gopls/internal/lsp/definition.go @@ -34,7 +34,7 @@ func (s *Server) definition(ctx context.Context, params *protocol.DefinitionPara switch kind := snapshot.FileKind(fh); kind { case source.Tmpl: return template.Definition(snapshot, fh, params.Position) - case source.Go: + case source.Go, source.Gop: // goxls: Go+ return source.Definition(ctx, snapshot, fh, params.Position) default: return nil, fmt.Errorf("can't find definitions for file type %s", kind) @@ -52,7 +52,7 @@ func (s *Server) typeDefinition(ctx context.Context, params *protocol.TypeDefini return nil, err } switch kind := snapshot.FileKind(fh); kind { - case source.Go: + case source.Go, source.Gop: // goxls: Go+ return source.TypeDefinition(ctx, snapshot, fh, params.Position) default: return nil, fmt.Errorf("can't find type definitions for file type %s", kind) diff --git a/gopls/internal/lsp/difinition_gox.go b/gopls/internal/lsp/difinition_gox.go new file mode 100644 index 00000000000..e598d3b34fc --- /dev/null +++ b/gopls/internal/lsp/difinition_gox.go @@ -0,0 +1,5 @@ +// Copyright 2023 The GoPlus Authors (goplus.org). All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package lsp diff --git a/gopls/internal/lsp/format.go b/gopls/internal/lsp/format.go index a6197a68e59..f6184e16682 100644 --- a/gopls/internal/lsp/format.go +++ b/gopls/internal/lsp/format.go @@ -29,6 +29,8 @@ func (s *Server) formatting(ctx context.Context, params *protocol.DocumentFormat return mod.Format(ctx, snapshot, fh) case source.Go: return source.Format(ctx, snapshot, fh) + case source.Gop: // goxls: format Go+ + return source.FormatGop(ctx, snapshot, fh) case source.Work: return work.Format(ctx, snapshot, fh) } diff --git a/gopls/internal/lsp/format_gox.go b/gopls/internal/lsp/format_gox.go new file mode 100644 index 00000000000..e598d3b34fc --- /dev/null +++ b/gopls/internal/lsp/format_gox.go @@ -0,0 +1,5 @@ +// Copyright 2023 The GoPlus Authors (goplus.org). All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package lsp diff --git a/gopls/internal/lsp/source/format_gox.go b/gopls/internal/lsp/source/format_gox.go new file mode 100644 index 00000000000..b63a8825e91 --- /dev/null +++ b/gopls/internal/lsp/source/format_gox.go @@ -0,0 +1,98 @@ +// Copyright 2023 The GoPlus Authors (goplus.org). All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package source + +import ( + "bytes" + "context" + "fmt" + "path/filepath" + + "github.com/goplus/gop/format" + "golang.org/x/tools/gopls/internal/goxls/goputil" + "golang.org/x/tools/gopls/internal/lsp/protocol" + "golang.org/x/tools/internal/event" + "golang.org/x/tools/internal/tokeninternal" +) + +// FormatGop formats a file with a given range. +func FormatGop(ctx context.Context, snapshot Snapshot, fh FileHandle) ([]protocol.TextEdit, error) { + ctx, done := event.Start(ctx, "gop.Format") + defer done() + + // Generated files shouldn't be edited. So, don't format them + if IsGenerated(ctx, snapshot, fh.URI()) { + return nil, fmt.Errorf("can't format %q: file is generated", fh.URI().Filename()) + } + + pgf, err := snapshot.ParseGo(ctx, fh, ParseFull) + if err != nil { + return nil, err + } + // Even if this file has parse errors, it might still be possible to format it. + // Using format.Node on an AST with errors may result in code being modified. + // Attempt to format the source of this file instead. + if pgf.ParseErr != nil { + formatted, err := formatGopSource(ctx, fh) + if err != nil { + return nil, err + } + return computeTextEdits(ctx, snapshot, pgf, string(formatted)) + } + + // format.Node changes slightly from one release to another, so the version + // of Go used to build the LSP server will determine how it formats code. + // This should be acceptable for all users, who likely be prompted to rebuild + // the LSP server on each Go release. + buf := &bytes.Buffer{} + fset := tokeninternal.FileSetFor(pgf.Tok) + if err := format.Node(buf, fset, pgf.File); err != nil { + return nil, err + } + formatted := buf.String() + + // Apply additional formatting, if any is supported. Currently, the only + // supported additional formatter is gofumpt. + if format := snapshot.Options().GofumptFormat; snapshot.Options().Gofumpt && format != nil { + // gofumpt can customize formatting based on language version and module + // path, if available. + // + // Try to derive this information, but fall-back on the default behavior. + // + // TODO: under which circumstances can we fail to find module information? + // Can this, for example, result in inconsistent formatting across saves, + // due to pending calls to packages.Load? + var langVersion, modulePath string + meta, err := NarrowestMetadataForFile(ctx, snapshot, fh.URI()) + if err == nil { + if mi := meta.Module; mi != nil { + langVersion = mi.GoVersion + modulePath = mi.Path + } + } + b, err := format(ctx, langVersion, modulePath, buf.Bytes()) + if err != nil { + return nil, err + } + formatted = string(b) + } + return computeTextEdits(ctx, snapshot, pgf, formatted) +} + +func formatGopSource(ctx context.Context, fh FileHandle) ([]byte, error) { + _, done := event.Start(ctx, "gop.formatSource") + defer done() + + data, err := fh.Content() + if err != nil { + return nil, err + } + return format.Source(data, isClass(fh)) +} + +func isClass(fh FileHandle) bool { + fext := filepath.Ext(fh.URI().Filename()) + return goputil.FileKind(fext) == goputil.FileGopClass +}