mirror of
https://github.com/golang/go
synced 2024-11-18 10:14:45 -07:00
internal/lsp/source: format fill_struct.go
Change-Id: Iaf238dfba6f51aed0a15f6ef2b231e1a1816f683 Reviewed-on: https://go-review.googlesource.com/c/tools/+/237421 Run-TryBot: Heschi Kreinick <heschi@google.com> TryBot-Result: Gobot Gobot <gobot@golang.org> Reviewed-by: Rebecca Stambler <rstambler@golang.org>
This commit is contained in:
parent
2ee2e4c56a
commit
1fdcbd1300
@ -1,144 +1,144 @@
|
||||
// Copyright 2020 The Go Authors. 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 (
|
||||
"context"
|
||||
"fmt"
|
||||
"go/format"
|
||||
"go/types"
|
||||
"strings"
|
||||
|
||||
"golang.org/x/tools/go/ast/astutil"
|
||||
"golang.org/x/tools/internal/lsp/protocol"
|
||||
)
|
||||
|
||||
// FillStruct completes all of targeted struct's fields with their default values.
|
||||
func FillStruct(ctx context.Context, snapshot Snapshot, fh FileHandle, protoRng protocol.Range) ([]protocol.CodeAction, error) {
|
||||
|
||||
pkg, pgh, err := getParsedFile(ctx, snapshot, fh, NarrowestPackageHandle)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("getting file for struct fill code action: %v", err)
|
||||
}
|
||||
file, src, m, _, err := pgh.Cached()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
spn, err := m.PointSpan(protoRng.Start)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
spanRng, err := spn.Range(m.Converter)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
path, _ := astutil.PathEnclosingInterval(file, spanRng.Start, spanRng.End)
|
||||
if path == nil {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
ecl := enclosingCompositeLiteral(path, spanRng.Start, pkg.GetTypesInfo())
|
||||
if ecl == nil || !ecl.isStruct() {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// If in F{ Bar<> : V} or anywhere in F{Bar : V, ...}
|
||||
// we should not fill the struct.
|
||||
if ecl.inKey || len(ecl.cl.Elts) != 0 {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
var codeActions []protocol.CodeAction
|
||||
qfFunc := qualifier(file, pkg.GetTypes(), pkg.GetTypesInfo())
|
||||
switch obj := ecl.clType.(type) {
|
||||
case *types.Struct:
|
||||
fieldCount := obj.NumFields()
|
||||
if fieldCount == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
var fieldSourceCode strings.Builder
|
||||
for i := 0; i < fieldCount; i++ {
|
||||
field := obj.Field(i)
|
||||
// Ignore fields that are not accessible in the current package.
|
||||
if field.Pkg() != nil && field.Pkg() != pkg.GetTypes() && !field.Exported() {
|
||||
continue
|
||||
}
|
||||
|
||||
label := field.Name()
|
||||
value := formatZeroValue(field.Type(), qfFunc)
|
||||
fieldSourceCode.WriteString("\n")
|
||||
fieldSourceCode.WriteString(label)
|
||||
fieldSourceCode.WriteString(" : ")
|
||||
fieldSourceCode.WriteString(value)
|
||||
fieldSourceCode.WriteString(",")
|
||||
}
|
||||
|
||||
if fieldSourceCode.Len() == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
fieldSourceCode.WriteString("\n")
|
||||
|
||||
// the range of all text between '<>', inclusive. E.g. {<> ... <}>
|
||||
mappedRange := newMappedRange(snapshot.View().Session().Cache().FileSet(), m, ecl.cl.Lbrace, ecl.cl.Rbrace+1)
|
||||
protoRange, err := mappedRange.Range()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// consider formatting from the first character of the line the lbrace is on.
|
||||
// ToOffset is 1-based
|
||||
beginOffset, err := m.Converter.ToOffset(int(protoRange.Start.Line)+1, 1)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
endOffset, err := m.Converter.ToOffset(int(protoRange.Start.Line)+1, int(protoRange.Start.Character)+1)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// An increment to make sure the lbrace is included in the slice.
|
||||
endOffset++
|
||||
// Append the edits. Then append the closing brace.
|
||||
var newSourceCode strings.Builder
|
||||
newSourceCode.Grow(endOffset - beginOffset + fieldSourceCode.Len() + 1)
|
||||
newSourceCode.WriteString(string(src[beginOffset:endOffset]))
|
||||
newSourceCode.WriteString(fieldSourceCode.String())
|
||||
newSourceCode.WriteString("}")
|
||||
|
||||
buf, err := format.Source([]byte(newSourceCode.String()))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// it is guaranteed that a left brace exists.
|
||||
var edit = string(buf[strings.IndexByte(string(buf), '{'):])
|
||||
|
||||
codeActions = append(codeActions, protocol.CodeAction{
|
||||
Title: "Fill struct",
|
||||
Kind: protocol.RefactorRewrite,
|
||||
Edit: protocol.WorkspaceEdit{
|
||||
DocumentChanges: []protocol.TextDocumentEdit{
|
||||
{
|
||||
TextDocument: protocol.VersionedTextDocumentIdentifier{
|
||||
Version: fh.Identity().Version,
|
||||
TextDocumentIdentifier: protocol.TextDocumentIdentifier{
|
||||
URI: protocol.URIFromSpanURI(fh.Identity().URI),
|
||||
},
|
||||
},
|
||||
Edits: []protocol.TextEdit{
|
||||
{
|
||||
Range: protoRange,
|
||||
NewText: edit,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
return codeActions, nil
|
||||
}
|
||||
// Copyright 2020 The Go Authors. 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 (
|
||||
"context"
|
||||
"fmt"
|
||||
"go/format"
|
||||
"go/types"
|
||||
"strings"
|
||||
|
||||
"golang.org/x/tools/go/ast/astutil"
|
||||
"golang.org/x/tools/internal/lsp/protocol"
|
||||
)
|
||||
|
||||
// FillStruct completes all of targeted struct's fields with their default values.
|
||||
func FillStruct(ctx context.Context, snapshot Snapshot, fh FileHandle, protoRng protocol.Range) ([]protocol.CodeAction, error) {
|
||||
|
||||
pkg, pgh, err := getParsedFile(ctx, snapshot, fh, NarrowestPackageHandle)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("getting file for struct fill code action: %v", err)
|
||||
}
|
||||
file, src, m, _, err := pgh.Cached()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
spn, err := m.PointSpan(protoRng.Start)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
spanRng, err := spn.Range(m.Converter)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
path, _ := astutil.PathEnclosingInterval(file, spanRng.Start, spanRng.End)
|
||||
if path == nil {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
ecl := enclosingCompositeLiteral(path, spanRng.Start, pkg.GetTypesInfo())
|
||||
if ecl == nil || !ecl.isStruct() {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// If in F{ Bar<> : V} or anywhere in F{Bar : V, ...}
|
||||
// we should not fill the struct.
|
||||
if ecl.inKey || len(ecl.cl.Elts) != 0 {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
var codeActions []protocol.CodeAction
|
||||
qfFunc := qualifier(file, pkg.GetTypes(), pkg.GetTypesInfo())
|
||||
switch obj := ecl.clType.(type) {
|
||||
case *types.Struct:
|
||||
fieldCount := obj.NumFields()
|
||||
if fieldCount == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
var fieldSourceCode strings.Builder
|
||||
for i := 0; i < fieldCount; i++ {
|
||||
field := obj.Field(i)
|
||||
// Ignore fields that are not accessible in the current package.
|
||||
if field.Pkg() != nil && field.Pkg() != pkg.GetTypes() && !field.Exported() {
|
||||
continue
|
||||
}
|
||||
|
||||
label := field.Name()
|
||||
value := formatZeroValue(field.Type(), qfFunc)
|
||||
fieldSourceCode.WriteString("\n")
|
||||
fieldSourceCode.WriteString(label)
|
||||
fieldSourceCode.WriteString(" : ")
|
||||
fieldSourceCode.WriteString(value)
|
||||
fieldSourceCode.WriteString(",")
|
||||
}
|
||||
|
||||
if fieldSourceCode.Len() == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
fieldSourceCode.WriteString("\n")
|
||||
|
||||
// the range of all text between '<>', inclusive. E.g. {<> ... <}>
|
||||
mappedRange := newMappedRange(snapshot.View().Session().Cache().FileSet(), m, ecl.cl.Lbrace, ecl.cl.Rbrace+1)
|
||||
protoRange, err := mappedRange.Range()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// consider formatting from the first character of the line the lbrace is on.
|
||||
// ToOffset is 1-based
|
||||
beginOffset, err := m.Converter.ToOffset(int(protoRange.Start.Line)+1, 1)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
endOffset, err := m.Converter.ToOffset(int(protoRange.Start.Line)+1, int(protoRange.Start.Character)+1)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// An increment to make sure the lbrace is included in the slice.
|
||||
endOffset++
|
||||
// Append the edits. Then append the closing brace.
|
||||
var newSourceCode strings.Builder
|
||||
newSourceCode.Grow(endOffset - beginOffset + fieldSourceCode.Len() + 1)
|
||||
newSourceCode.WriteString(string(src[beginOffset:endOffset]))
|
||||
newSourceCode.WriteString(fieldSourceCode.String())
|
||||
newSourceCode.WriteString("}")
|
||||
|
||||
buf, err := format.Source([]byte(newSourceCode.String()))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// it is guaranteed that a left brace exists.
|
||||
var edit = string(buf[strings.IndexByte(string(buf), '{'):])
|
||||
|
||||
codeActions = append(codeActions, protocol.CodeAction{
|
||||
Title: "Fill struct",
|
||||
Kind: protocol.RefactorRewrite,
|
||||
Edit: protocol.WorkspaceEdit{
|
||||
DocumentChanges: []protocol.TextDocumentEdit{
|
||||
{
|
||||
TextDocument: protocol.VersionedTextDocumentIdentifier{
|
||||
Version: fh.Identity().Version,
|
||||
TextDocumentIdentifier: protocol.TextDocumentIdentifier{
|
||||
URI: protocol.URIFromSpanURI(fh.Identity().URI),
|
||||
},
|
||||
},
|
||||
Edits: []protocol.TextEdit{
|
||||
{
|
||||
Range: protoRange,
|
||||
NewText: edit,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
return codeActions, nil
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user