Skip to content

Commit

Permalink
gopls/internal/lsp/source/completion: recheckOverload support found g…
Browse files Browse the repository at this point in the history
…opo overload decl from go
  • Loading branch information
visualfc committed Mar 21, 2024
1 parent 4c67aac commit 17f3133
Show file tree
Hide file tree
Showing 3 changed files with 301 additions and 240 deletions.
182 changes: 0 additions & 182 deletions gopls/internal/lsp/source/completion/completion.go
Original file line number Diff line number Diff line change
Expand Up @@ -3308,185 +3308,3 @@ func forEachPackageMember(content []byte, f func(tok token.Token, id *ast.Ident,
}
}
}

// goxls: quickParse
func (c *gopCompleter) quickParse(ctx context.Context, cMu *sync.Mutex, enough *int32, selName string, relevances map[string]float64, needImport bool, recheck *unimportChecked) func(uri span.URI, m *source.Metadata) error {
return func(uri span.URI, m *source.Metadata) error {
if atomic.LoadInt32(enough) != 0 {
return nil
}

fh, err := c.snapshot.ReadFile(ctx, uri)
if err != nil {
return err
}
content, err := fh.Content()
if err != nil {
return err
}
path := string(m.PkgPath)
forEachPackageMember(content, func(tok token.Token, id *ast.Ident, fn *ast.FuncDecl) {
if atomic.LoadInt32(enough) != 0 {
return
}

if !id.IsExported() {
return
}

cMu.Lock()
score := c.matcher.Score(id.Name)
cMu.Unlock()

if selName != "_" && score == 0 {
return // not a match; avoid constructing the completion item below
}

// The only detail is the kind and package: `var (from "example.com/foo")`
// TODO(adonovan): pretty-print FuncDecl.FuncType or TypeSpec.Type?
// TODO(adonovan): should this score consider the actual c.matcher.Score
// of the item? How does this compare with the deepState.enqueue path?
item := CompletionItem{
Label: id.Name,
Detail: fmt.Sprintf("%s (from %q)", strings.ToLower(tok.String()), m.PkgPath),
InsertText: id.Name,
Score: unimportedScore(relevances[path]),
}
switch tok {
case token.FUNC:
item.Kind = protocol.FunctionCompletion
case token.VAR:
item.Kind = protocol.VariableCompletion
case token.CONST:
item.Kind = protocol.ConstantCompletion
case token.TYPE:
// Without types, we can't distinguish Class from Interface.
item.Kind = protocol.ClassCompletion
}

if needImport {
imp := &importInfo{importPath: path}
if imports.ImportPathToAssumedName(path) != string(m.Name) {
imp.name = string(m.Name)
}
item.AdditionalTextEdits, _ = c.importEdits(imp)
}

// For functions, add a parameter snippet.
if fn != nil {
var sn snippet.Builder
sn.WriteText(id.Name)

paramList := func(open, close string, list *ast.FieldList) {
if list != nil {
var cfg printer.Config // slight overkill
var nparams int
param := func(name string, typ ast.Expr) {
if nparams > 0 {
sn.WriteText(", ")
}
nparams++
if c.opts.placeholders {
sn.WritePlaceholder(func(b *snippet.Builder) {
var buf strings.Builder
buf.WriteString(name)
buf.WriteByte(' ')
cfg.Fprint(&buf, token.NewFileSet(), typ)
b.WriteText(buf.String())
})
} else {
sn.WriteText(name)
}
}

sn.WriteText(open)
for _, field := range list.List {
if field.Names != nil {
for _, name := range field.Names {
param(name.Name, field.Type)
}
} else {
param("_", field.Type)
}
}
sn.WriteText(close)
}
}

paramList("[", "]", typeparams.ForFuncType(fn.Type))
paramList("(", ")", fn.Type.Params)

item.snippet = &sn
}

cMu.Lock()
if tok == token.CONST {
if id.Name == "GopPackage" {
recheck.pkgs[m.PkgPath] = true
} else if strings.HasPrefix(id.Name, "Gopo_") {
md := checkTypeMethod(id.Name[5:])
if md.typ == "" && ast.IsExported(md.name) {
item := CompletionItem{
Label: md.name,
Detail: fmt.Sprintf("func (from %q)", m.PkgPath),
InsertText: md.name,
Score: unimportedScore(relevances[path]),
Kind: protocol.FunctionCompletion,
isOverload: true,
}
if needImport {
imp := &importInfo{importPath: path}
if imports.ImportPathToAssumedName(path) != string(m.Name) {
imp.name = string(m.Name)
}
item.AdditionalTextEdits, _ = c.importEdits(imp)
}
recheck.gopo[m.PkgPath] = append(recheck.gopo[m.PkgPath], item)
}
}
}
if tok == token.FUNC {
noSnip := len(fn.Type.Params.List) == 0
if maybleIndexOverload(id.Name) {
recheck.items[m.PkgPath] = append(recheck.items[m.PkgPath], recheckItem{item, noSnip})
} else {
c.items = append(c.items, item)
// goxls func alias
if alias, ok := hasAliasName(id.Name); ok {
c.items = append(c.items, cloneAliasItem(item, id.Name, alias, 0.0001, noSnip))
}
}
} else {
c.items = append(c.items, item)
}
if len(c.items) >= unimportedMemberTarget {
atomic.StoreInt32(enough, 1)
}
cMu.Unlock()
})
return nil
}
}

type mthd struct {
typ string
name string
}

// Func (no _ func name)
// _Func (with _ func name)
// TypeName_Method (no _ method name)
// _TypeName__Method (with _ method name)
func checkTypeMethod(name string) mthd {
if pos := strings.IndexByte(name, '_'); pos >= 0 {
if pos == 0 {
t := name[1:]
if pos = strings.Index(t, "__"); pos <= 0 {
return mthd{"", t} // _Func
}
return mthd{t[:pos], t[pos+2:]} // _TypeName__Method
}
return mthd{name[:pos], name[pos+1:]} // TypeName_Method
}
return mthd{"", name} // Func
}
59 changes: 1 addition & 58 deletions gopls/internal/lsp/source/completion/completion_gox.go
Original file line number Diff line number Diff line change
Expand Up @@ -1306,11 +1306,7 @@ func (c *gopCompleter) selector(ctx context.Context, sel *ast.SelectorExpr) erro
}

// recheck is gop index overload check
recheck := &unimportChecked{
make(map[source.PackagePath]bool),
make(map[source.PackagePath][]recheckItem),
make(map[source.PackagePath][]CompletionItem),
}
recheck := newRecheckOverload()
for _, path := range paths {
m := known[source.PackagePath(path)]
if len(m.CompiledGopFiles) > 0 {
Expand Down Expand Up @@ -1375,11 +1371,6 @@ func (c *gopCompleter) selector(ctx context.Context, sel *ast.SelectorExpr) erro
return nil
}

type recheckItem struct {
CompletionItem
noSnip bool
}

func (c *gopCompleter) packageMembers(pkg *types.Package, score float64, imp *importInfo, cb func(candidate)) {
scope := pkg.Scope()
for _, name := range scope.Names() {
Expand Down Expand Up @@ -2485,51 +2476,3 @@ func gopForEachPackageMember(content []byte, f func(tok token.Token, id *ast.Ide
}
}
}

type unimportChecked struct {
pkgs map[source.PackagePath]bool // gop package
items map[source.PackagePath][]recheckItem // index overload funcs
gopo map[source.PackagePath][]CompletionItem // gopo overload funcs
}

func (recheck *unimportChecked) checkOverload(c *gopCompleter) {
// check gop package index overload
for pkg, items := range recheck.items {
if recheck.pkgs[pkg] {
names := make(map[string]bool)
sort.Slice(items, func(i, j int) bool {
return items[i].Label < items[j].Label
})
for _, item := range items {
id := item.Label[:len(item.Label)-3]
if !names[id] {
names[id] = true
item.isOverload = true
item.Detail = "Go+ overload func\n\n" + item.Detail
c.items = append(c.items, cloneAliasItem(item.CompletionItem, item.Label, id, 0, false))
if alias, ok := hasAliasName(id); ok {
c.items = append(c.items, cloneAliasItem(item.CompletionItem, item.Label, alias, 0.0001, item.noSnip))
}
}
}
} else {
for _, item := range items {
c.items = append(c.items, item.CompletionItem)
if alias, ok := hasAliasName(item.Label); ok {
c.items = append(c.items, cloneAliasItem(item.CompletionItem, item.Label, alias, 0.0001, item.noSnip))
}
}
}
}
// check gop package gopo overload
for pkg, items := range recheck.gopo {
if recheck.pkgs[pkg] {
for _, item := range items {
c.items = append(c.items, item)
if alias, ok := hasAliasName(item.Label); ok {
c.items = append(c.items, cloneAliasItem(item, item.Label, alias, 0.0001, true))
}
}
}
}
}
Loading

0 comments on commit 17f3133

Please sign in to comment.