From c643c5675e65b2856e05c60658dbaa5d6d012fd7 Mon Sep 17 00:00:00 2001 From: visualfc Date: Sat, 3 Feb 2024 17:01:23 +0800 Subject: [PATCH] gopls/internal/lsp/source/completion: unimport check gop index overload --- .../lsp/source/completion/completion.go | 37 ++++++++------ .../lsp/source/completion/completion_gox.go | 49 ++++++++++++++++++- .../lsp/source/completion/format_gox.go | 11 +++++ 3 files changed, 81 insertions(+), 16 deletions(-) diff --git a/gopls/internal/lsp/source/completion/completion.go b/gopls/internal/lsp/source/completion/completion.go index 541f5a6c381..9398da502ef 100644 --- a/gopls/internal/lsp/source/completion/completion.go +++ b/gopls/internal/lsp/source/completion/completion.go @@ -3310,7 +3310,7 @@ 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) func(uri span.URI, m *source.Metadata) error { +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 @@ -3420,23 +3420,32 @@ func (c *gopCompleter) quickParse(ctx context.Context, cMu *sync.Mutex, enough * } cMu.Lock() - c.items = append(c.items, item) - // goxls func alias + if tok == token.CONST && id.Name == "GopPackage" { + recheck.pkgs[m.PkgPath] = true + } if tok == token.FUNC { - if alias, ok := hasAliasName(id.Name); ok { - var noSnip bool - switch len(fn.Type.Params.List) { - case 0: - noSnip = true - case 1: - if fn.Recv != nil { - if _, ok := fn.Type.Params.List[0].Type.(*ast.Ellipsis); ok { - noSnip = true - } + var noSnip bool + switch len(fn.Type.Params.List) { + case 0: + noSnip = true + case 1: + if fn.Recv != nil { + if _, ok := fn.Type.Params.List[0].Type.(*ast.Ellipsis); ok { + noSnip = true } } - c.items = append(c.items, cloneAliasItem(item, id.Name, alias, 0.0001, noSnip)) } + 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) diff --git a/gopls/internal/lsp/source/completion/completion_gox.go b/gopls/internal/lsp/source/completion/completion_gox.go index f137be3478c..dc98c3fc418 100644 --- a/gopls/internal/lsp/source/completion/completion_gox.go +++ b/gopls/internal/lsp/source/completion/completion_gox.go @@ -1310,12 +1310,20 @@ func (c *gopCompleter) selector(ctx context.Context, sel *ast.SelectorExpr) erro return nil } + // recheck is gop index overload check + recheck := &unimportChecked{ + make(map[source.PackagePath]bool), + make(map[source.PackagePath][]recheckItem), + } + for _, path := range paths { + recheck.pkgs[source.PackagePath(path)] = true + } // Extract the package-level candidates using a quick parse. - quickParseGo := c.quickParse(ctx, &cMu, &enough, sel.Sel.Name, relevances, needImport) + quickParseGo := c.quickParse(ctx, &cMu, &enough, sel.Sel.Name, relevances, needImport, recheck) var g errgroup.Group for _, path := range paths { m := known[source.PackagePath(path)] - for _, uri := range m.CompiledGopFiles { // goxls: TODO - how to handle Go files? + for _, uri := range m.CompiledGopFiles { uri := uri g.Go(func() error { return quickParse(uri, m) @@ -1331,6 +1339,33 @@ func (c *gopCompleter) selector(ctx context.Context, sel *ast.SelectorExpr) erro if err := g.Wait(); err != nil { return err } + // check gop packages 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 + 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)) + } + } + } + } // In addition, we search in the module cache using goimports. ctx, cancel := context.WithCancel(ctx) @@ -1368,6 +1403,16 @@ func (c *gopCompleter) selector(ctx context.Context, sel *ast.SelectorExpr) erro return nil } +type recheckItem struct { + CompletionItem + noSnip bool +} + +type unimportChecked struct { + pkgs map[source.PackagePath]bool // gop package + items map[source.PackagePath][]recheckItem +} + func (c *gopCompleter) packageMembers(pkg *types.Package, score float64, imp *importInfo, cb func(candidate)) { scope := pkg.Scope() for _, name := range scope.Names() { diff --git a/gopls/internal/lsp/source/completion/format_gox.go b/gopls/internal/lsp/source/completion/format_gox.go index 9078fc8b000..529b635dba8 100644 --- a/gopls/internal/lsp/source/completion/format_gox.go +++ b/gopls/internal/lsp/source/completion/format_gox.go @@ -295,6 +295,17 @@ func isIndexOverload(fn string, name string) bool { return false } +func maybleIndexOverload(fn string) bool { + n := len(fn) + if (n > 3) && (fn[n-3] == '_') && (fn[n-2] == '_') { + c := fn[n-1] + if (c >= '0' && c <= '9') || (c >= 'a' && c <= 'z') { + return true + } + } + return false +} + func (c *gopCompleter) formatBuiltin(ctx context.Context, cand candidate) (CompletionItem, error) { obj := cand.obj item := CompletionItem{