mirror of
https://github.com/golang/go
synced 2024-11-05 20:26:13 -07:00
3f7dfa39cf
I want to stop sorting unimported completions. We still want to show users something reasonable, so use label as a tiebreaker for score in the higher level completion function. To maintain the current sorting, we need to adjust scores by search depth (height?) for lexical completions. A few tests are really ties, and need sorting in the test case. Change-Id: Ie2d09fdcbebf6fda4ab33a2f16c579d12b0f26ad Reviewed-on: https://go-review.googlesource.com/c/tools/+/212633 Run-TryBot: Heschi Kreinick <heschi@google.com> TryBot-Result: Gobot Gobot <gobot@golang.org> Reviewed-by: Rebecca Stambler <rstambler@golang.org>
113 lines
2.8 KiB
Go
113 lines
2.8 KiB
Go
// Copyright 2019 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 (
|
|
"go/ast"
|
|
"go/token"
|
|
"math"
|
|
)
|
|
|
|
type labelType int
|
|
|
|
const (
|
|
labelNone labelType = iota
|
|
labelBreak
|
|
labelContinue
|
|
labelGoto
|
|
)
|
|
|
|
// wantLabelCompletion returns true if we want (only) label
|
|
// completions at the position.
|
|
func (c *completer) wantLabelCompletion() labelType {
|
|
if _, ok := c.path[0].(*ast.Ident); ok && len(c.path) > 1 {
|
|
// We want a label if we are an *ast.Ident child of a statement
|
|
// that accepts a label, e.g. "break Lo<>".
|
|
return takesLabel(c.path[1])
|
|
}
|
|
|
|
return labelNone
|
|
}
|
|
|
|
// takesLabel returns the corresponding labelType if n is a statement
|
|
// that accepts a label, otherwise labelNone.
|
|
func takesLabel(n ast.Node) labelType {
|
|
if bs, ok := n.(*ast.BranchStmt); ok {
|
|
switch bs.Tok {
|
|
case token.BREAK:
|
|
return labelBreak
|
|
case token.CONTINUE:
|
|
return labelContinue
|
|
case token.GOTO:
|
|
return labelGoto
|
|
}
|
|
}
|
|
return labelNone
|
|
}
|
|
|
|
// labels adds completion items for labels defined in the enclosing
|
|
// function.
|
|
func (c *completer) labels(lt labelType) {
|
|
if c.enclosingFunc == nil {
|
|
return
|
|
}
|
|
|
|
addLabel := func(score float64, l *ast.LabeledStmt) {
|
|
labelObj := c.pkg.GetTypesInfo().ObjectOf(l.Label)
|
|
if labelObj != nil {
|
|
c.found(candidate{obj: labelObj, score: score})
|
|
}
|
|
}
|
|
|
|
switch lt {
|
|
case labelBreak, labelContinue:
|
|
// "break" and "continue" only accept labels from enclosing statements.
|
|
|
|
for i, p := range c.path {
|
|
switch p := p.(type) {
|
|
case *ast.FuncLit:
|
|
// Labels are function scoped, so don't continue out of functions.
|
|
return
|
|
case *ast.LabeledStmt:
|
|
switch p.Stmt.(type) {
|
|
case *ast.ForStmt, *ast.RangeStmt:
|
|
// Loop labels can be used for "break" or "continue".
|
|
addLabel(highScore*math.Pow(.99, float64(i)), p)
|
|
case *ast.SwitchStmt, *ast.SelectStmt, *ast.TypeSwitchStmt:
|
|
// Switch and select labels can be used only for "break".
|
|
if lt == labelBreak {
|
|
addLabel(highScore*math.Pow(.99, float64(i)), p)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
case labelGoto:
|
|
// Goto accepts any label in the same function not in a nested
|
|
// block. It also doesn't take labels that would jump across
|
|
// variable definitions, but ignore that case for now.
|
|
ast.Inspect(c.enclosingFunc.body, func(n ast.Node) bool {
|
|
if n == nil {
|
|
return false
|
|
}
|
|
|
|
switch n := n.(type) {
|
|
// Only search into block-like nodes enclosing our "goto".
|
|
// This prevents us from finding labels in nested blocks.
|
|
case *ast.BlockStmt, *ast.CommClause, *ast.CaseClause:
|
|
for _, p := range c.path {
|
|
if n == p {
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
case *ast.LabeledStmt:
|
|
addLabel(highScore, n)
|
|
}
|
|
|
|
return true
|
|
})
|
|
}
|
|
}
|