X-Git-Url: https://git.josue.xyz/?a=blobdiff_plain;f=.config%2Fcoc%2Fextensions%2Fcoc-go-data%2Ftools%2Fpkg%2Fmod%2Fgolang.org%2Fx%2Ftools%40v0.0.0-20201105173854-bc9fc8d8c4bc%2Finternal%2Flsp%2Fsource%2Fcompletion%2Fstatements.go;fp=.config%2Fcoc%2Fextensions%2Fcoc-go-data%2Ftools%2Fpkg%2Fmod%2Fgolang.org%2Fx%2Ftools%40v0.0.0-20201105173854-bc9fc8d8c4bc%2Finternal%2Flsp%2Fsource%2Fcompletion%2Fstatements.go;h=62d3cf0ed0808c4bebf9ff324a54e3d43a42587d;hb=4d07c77cf4d78cab8639e13ddc3c22495e585b0b;hp=0000000000000000000000000000000000000000;hpb=b3950616b54221c40a7dab9099bda675007e5b6e;p=dotfiles%2F.git diff --git a/.config/coc/extensions/coc-go-data/tools/pkg/mod/golang.org/x/tools@v0.0.0-20201105173854-bc9fc8d8c4bc/internal/lsp/source/completion/statements.go b/.config/coc/extensions/coc-go-data/tools/pkg/mod/golang.org/x/tools@v0.0.0-20201105173854-bc9fc8d8c4bc/internal/lsp/source/completion/statements.go new file mode 100644 index 00000000..62d3cf0e --- /dev/null +++ b/.config/coc/extensions/coc-go-data/tools/pkg/mod/golang.org/x/tools@v0.0.0-20201105173854-bc9fc8d8c4bc/internal/lsp/source/completion/statements.go @@ -0,0 +1,305 @@ +// 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 completion + +import ( + "fmt" + "go/ast" + "go/token" + "go/types" + + "golang.org/x/tools/internal/lsp/protocol" + "golang.org/x/tools/internal/lsp/snippet" + "golang.org/x/tools/internal/lsp/source" +) + +// addStatementCandidates adds full statement completion candidates +// appropriate for the current context. +func (c *completer) addStatementCandidates() { + c.addErrCheckAndReturn() + c.addAssignAppend() +} + +// addAssignAppend offers a completion candidate of the form: +// +// someSlice = append(someSlice, ) +// +// It will offer the "append" completion in two situations: +// +// 1. Position is in RHS of assign, prefix matches "append", and +// corresponding LHS object is a slice. For example, +// "foo = ap<>" completes to "foo = append(foo, )". +// +// Or +// +// 2. Prefix is an ident or selector in an *ast.ExprStmt (i.e. +// beginning of statement), and our best matching candidate is a +// slice. For example: "foo.ba" completes to "foo.bar = append(foo.bar, )". +func (c *completer) addAssignAppend() { + if len(c.path) < 3 { + return + } + + ident, _ := c.path[0].(*ast.Ident) + if ident == nil { + return + } + + var ( + // sliceText is the full name of our slice object, e.g. "s.abc" in + // "s.abc = app<>". + sliceText string + // needsLHS is true if we need to prepend the LHS slice name and + // "=" to our candidate. + needsLHS = false + fset = c.snapshot.FileSet() + ) + + switch n := c.path[1].(type) { + case *ast.AssignStmt: + // We are already in an assignment. Make sure our prefix matches "append". + if c.matcher.Score("append") <= 0 { + return + } + + exprIdx := exprAtPos(c.pos, n.Rhs) + if exprIdx == len(n.Rhs) || exprIdx > len(n.Lhs)-1 { + return + } + + lhsType := c.pkg.GetTypesInfo().TypeOf(n.Lhs[exprIdx]) + if lhsType == nil { + return + } + + // Make sure our corresponding LHS object is a slice. + if _, isSlice := lhsType.Underlying().(*types.Slice); !isSlice { + return + } + + // The name or our slice is whatever's in the LHS expression. + sliceText = source.FormatNode(fset, n.Lhs[exprIdx]) + case *ast.SelectorExpr: + // Make sure we are a selector at the beginning of a statement. + if _, parentIsExprtStmt := c.path[2].(*ast.ExprStmt); !parentIsExprtStmt { + return + } + + // So far we only know the first part of our slice name. For + // example in "s.a<>" we only know our slice begins with "s." + // since the user could still be typing. + sliceText = source.FormatNode(fset, n.X) + "." + needsLHS = true + case *ast.ExprStmt: + needsLHS = true + default: + return + } + + var ( + label string + snip snippet.Builder + score = highScore + ) + + if needsLHS { + // Offer the long form assign + append candidate if our best + // candidate is a slice. + bestItem := c.topCandidate() + if bestItem == nil || bestItem.obj == nil || bestItem.obj.Type() == nil { + return + } + + if _, isSlice := bestItem.obj.Type().Underlying().(*types.Slice); !isSlice { + return + } + + // Don't rank the full form assign + append candidate above the + // slice itself. + score = bestItem.Score - 0.01 + + // Fill in rest of sliceText now that we have the object name. + sliceText += bestItem.Label + + // Fill in the candidate's LHS bits. + label = fmt.Sprintf("%s = ", bestItem.Label) + snip.WriteText(label) + } + + snip.WriteText(fmt.Sprintf("append(%s, ", sliceText)) + snip.WritePlaceholder(nil) + snip.WriteText(")") + + c.items = append(c.items, CompletionItem{ + Label: label + fmt.Sprintf("append(%s, )", sliceText), + Kind: protocol.FunctionCompletion, + Score: score, + snippet: &snip, + }) +} + +// topCandidate returns the strictly highest scoring candidate +// collected so far. If the top two candidates have the same score, +// nil is returned. +func (c *completer) topCandidate() *CompletionItem { + var bestItem, secondBestItem *CompletionItem + for i := range c.items { + if bestItem == nil || c.items[i].Score > bestItem.Score { + bestItem = &c.items[i] + } else if secondBestItem == nil || c.items[i].Score > secondBestItem.Score { + secondBestItem = &c.items[i] + } + } + + // If secondBestItem has the same score, bestItem isn't + // the strict best. + if secondBestItem != nil && secondBestItem.Score == bestItem.Score { + return nil + } + + return bestItem +} + +// addErrCheckAndReturn offers a completion candidate of the form: +// +// if err != nil { +// return nil, err +// } +// +// The position must be in a function that returns an error, and the +// statement preceding the position must be an assignment where the +// final LHS object is an error. addErrCheckAndReturn will synthesize +// zero values as necessary to make the return statement valid. +func (c *completer) addErrCheckAndReturn() { + if len(c.path) < 2 || c.enclosingFunc == nil || !c.opts.placeholders { + return + } + + var ( + errorType = types.Universe.Lookup("error").Type() + result = c.enclosingFunc.sig.Results() + ) + // Make sure our enclosing function returns an error. + if result.Len() == 0 || !types.Identical(result.At(result.Len()-1).Type(), errorType) { + return + } + + prevLine := prevStmt(c.pos, c.path) + if prevLine == nil { + return + } + + // Make sure our preceding statement was as assignment. + assign, _ := prevLine.(*ast.AssignStmt) + if assign == nil || len(assign.Lhs) == 0 { + return + } + + lastAssignee := assign.Lhs[len(assign.Lhs)-1] + + // Make sure the final assignee is an error. + if !types.Identical(c.pkg.GetTypesInfo().TypeOf(lastAssignee), errorType) { + return + } + + var ( + // errText is e.g. "err" in "foo, err := bar()". + errText = source.FormatNode(c.snapshot.FileSet(), lastAssignee) + + // Whether we need to include the "if" keyword in our candidate. + needsIf = true + ) + + // "_" isn't a real object. + if errText == "_" { + return + } + + // Below we try to detect if the user has already started typing "if + // err" so we can replace what they've typed with our complete + // statement. + switch n := c.path[0].(type) { + case *ast.Ident: + switch c.path[1].(type) { + case *ast.ExprStmt: + // This handles: + // + // f, err := os.Open("foo") + // i<> + + // Make sure they are typing "if". + if c.matcher.Score("if") <= 0 { + return + } + case *ast.IfStmt: + // This handles: + // + // f, err := os.Open("foo") + // if er<> + + // Make sure they are typing the error's name. + if c.matcher.Score(errText) <= 0 { + return + } + + needsIf = false + default: + return + } + case *ast.IfStmt: + // This handles: + // + // f, err := os.Open("foo") + // if <> + + // Avoid false positives by ensuring the if's cond is a bad + // expression. For example, don't offer the completion in cases + // like "if <> somethingElse". + if _, bad := n.Cond.(*ast.BadExpr); !bad { + return + } + + // If "if" is our direct prefix, we need to include it in our + // candidate since the existing "if" will be overwritten. + needsIf = c.pos == n.Pos()+token.Pos(len("if")) + } + + // Build up a snippet that looks like: + // + // if err != nil { + // return , ..., ${1:err} + // } + // + // We make the error a placeholder so it is easy to alter the error. + var snip snippet.Builder + if needsIf { + snip.WriteText("if ") + } + snip.WriteText(fmt.Sprintf("%s != nil {\n\treturn ", errText)) + + for i := 0; i < result.Len()-1; i++ { + snip.WriteText(formatZeroValue(result.At(i).Type(), c.qf)) + snip.WriteText(", ") + } + + snip.WritePlaceholder(func(b *snippet.Builder) { + b.WriteText(errText) + }) + + snip.WriteText("\n}") + + label := fmt.Sprintf("%[1]s != nil { return %[1]s }", errText) + if needsIf { + label = "if " + label + } + + c.items = append(c.items, CompletionItem{ + Label: label, + // There doesn't seem to be a more appropriate kind. + Kind: protocol.KeywordCompletion, + Score: highScore, + snippet: &snip, + }) +}