From 1eb7cdb0258e354e8b2f61d703b08b3c1fbda753 Mon Sep 17 00:00:00 2001 From: Adrian Hesketh Date: Sun, 3 Sep 2023 19:44:47 +0100 Subject: [PATCH 1/7] fix(lsp): file incorrectly updated when importing modules, fixes #135 --- cmd/templ/lspcmd/proxy/server.go | 42 ++++++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) diff --git a/cmd/templ/lspcmd/proxy/server.go b/cmd/templ/lspcmd/proxy/server.go index 2dba884d5..d858db98f 100644 --- a/cmd/templ/lspcmd/proxy/server.go +++ b/cmd/templ/lspcmd/proxy/server.go @@ -338,11 +338,53 @@ func (p *Server) Completion(ctx context.Context, params *lsp.CompletionParams) ( if item.TextEdit != nil { item.TextEdit.Range = p.convertGoRangeToTemplRange(templURI, item.TextEdit.Range) } + if item.Kind == lsp.CompletionItemKindModule && len(item.AdditionalTextEdits) > 0 { + doc, ok := p.TemplSource.Get(string(templURI)) + if !ok { + continue + } + insertAtLine, multiLineImport, hasOtherImports := findLastImport(doc) + te := lsp.TextEdit{ + Range: lsp.Range{ + Start: lsp.Position{Line: uint32(insertAtLine), Character: 0}, + End: lsp.Position{Line: uint32(insertAtLine), Character: 0}, + }, + NewText: fmt.Sprintf("import \"%s\"", item.Label), + } + if multiLineImport { + te.NewText = fmt.Sprintf("\t\"%s\"\n", item.Label) + } + if !hasOtherImports { + te.NewText = "\n" + te.NewText + "\n" + } + item.AdditionalTextEdits = []lsp.TextEdit{te} + } result.Items[i] = item } return } +func findLastImport(document *Document) (insertAtLine int, multiLineImport bool, hasOtherImports bool) { + latestSingleLineImport := 1 + var line string + for insertAtLine, line = range document.Lines { + if strings.HasPrefix(line, "import (") { + multiLineImport = true + hasOtherImports = true + continue + } + if strings.HasPrefix(line, "import \"") { + latestSingleLineImport = insertAtLine + hasOtherImports = true + continue + } + if multiLineImport && strings.HasPrefix(line, ")") { + return + } + } + return latestSingleLineImport, false, hasOtherImports +} + func (p *Server) CompletionResolve(ctx context.Context, params *lsp.CompletionItem) (result *lsp.CompletionItem, err error) { p.Log.Info("client -> server: CompletionResolve") defer p.Log.Info("client -> server: CompletionResolve end") From 4b5baa2c154754a919417e3718e9da09655f04e3 Mon Sep 17 00:00:00 2001 From: Adrian Hesketh Date: Sun, 3 Sep 2023 19:59:00 +0100 Subject: [PATCH 2/7] refactor: reduce time spent iterating through files, and reduce risk of incorrectly identifying an import statement --- cmd/templ/lspcmd/proxy/server.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/cmd/templ/lspcmd/proxy/server.go b/cmd/templ/lspcmd/proxy/server.go index d858db98f..3f43bd1b0 100644 --- a/cmd/templ/lspcmd/proxy/server.go +++ b/cmd/templ/lspcmd/proxy/server.go @@ -381,6 +381,10 @@ func findLastImport(document *Document) (insertAtLine int, multiLineImport bool, if multiLineImport && strings.HasPrefix(line, ")") { return } + // Only add import statements before templates, functions, css, and script templates. + if strings.HasPrefix(line, "templ ") || strings.HasPrefix(line, "func ") || strings.HasPrefix(line, "css ") || strings.HasPrefix(line, "script ") { + break + } } return latestSingleLineImport, false, hasOtherImports } From 35659a2fbfc3fa63d4f1a889802d9da672025d5f Mon Sep 17 00:00:00 2001 From: Adrian Hesketh Date: Mon, 4 Sep 2023 10:10:25 +0100 Subject: [PATCH 3/7] refactor: add tests for import management --- cmd/templ/lspcmd/proxy/import_test.go | 264 ++++++++++++++++++++++++++ cmd/templ/lspcmd/proxy/server.go | 55 +++--- 2 files changed, 297 insertions(+), 22 deletions(-) create mode 100644 cmd/templ/lspcmd/proxy/import_test.go diff --git a/cmd/templ/lspcmd/proxy/import_test.go b/cmd/templ/lspcmd/proxy/import_test.go new file mode 100644 index 000000000..8011aafb0 --- /dev/null +++ b/cmd/templ/lspcmd/proxy/import_test.go @@ -0,0 +1,264 @@ +package proxy + +import ( + "strings" + "testing" + + "github.com/google/go-cmp/cmp" +) + +func TestFindLastImport(t *testing.T) { + tests := []struct { + name string + templContents string + packageName string + expected string + }{ + { + name: "if there are no imports, add a single line import", + templContents: `package main + +templ example() { +} +`, + packageName: "strings", + expected: `package main + +import "strings" + +templ example() { +} +`, + }, + { + name: "if there is an existing single-line imports, add one at the end", + templContents: `package main + +import "strings" + +templ example() { +} +`, + packageName: "fmt", + expected: `package main + +import "strings" +import "fmt" + +templ example() { +} +`, + }, + { + name: "if there are multiple existing single-line imports, add one at the end", + templContents: `package main + +import "strings" +import "fmt" + +templ example() { +} +`, + packageName: "time", + expected: `package main + +import "strings" +import "fmt" +import "time" + +templ example() { +} +`, + }, + { + name: "if there are existing multi-line imports, add one at the end", + templContents: `package main + +import ( + "strings" +) + +templ example() { +} +`, + packageName: "fmt", + expected: `package main + +import ( + "strings" + "fmt" +) + +templ example() { +} +`, + }, + { + name: "ignore imports that happen after templates", + templContents: `package main + +import "strings" + +templ example() { +} + +import "other" +`, + packageName: "fmt", + expected: `package main + +import "strings" +import "fmt" + +templ example() { +} + +import "other" +`, + }, + { + name: "ignore imports that happen after funcs in the file", + templContents: `package main + +import "strings" + +func example() { +} + +import "other" +`, + packageName: "fmt", + expected: `package main + +import "strings" +import "fmt" + +func example() { +} + +import "other" +`, + }, + { + name: "ignore imports that happen after css expressions in the file", + templContents: `package main + +import "strings" + +css example() { +} + +import "other" +`, + packageName: "fmt", + expected: `package main + +import "strings" +import "fmt" + +css example() { +} + +import "other" +`, + }, + { + name: "ignore imports that happen after script expressions in the file", + templContents: `package main + +import "strings" + +script example() { +} + +import "other" +`, + packageName: "fmt", + expected: `package main + +import "strings" +import "fmt" + +script example() { +} + +import "other" +`, + }, + { + name: "ignore imports that happen after var expressions in the file", + templContents: `package main + +import "strings" + +var s string + +import "other" +`, + packageName: "fmt", + expected: `package main + +import "strings" +import "fmt" + +var s string + +import "other" +`, + }, + { + name: "ignore imports that happen after const expressions in the file", + templContents: `package main + +import "strings" + +const s = "test" + +import "other" +`, + packageName: "fmt", + expected: `package main + +import "strings" +import "fmt" + +const s = "test" + +import "other" +`, + }, + { + name: "ignore imports that happen after type expressions in the file", + templContents: `package main + +import "strings" + +type Value int + +import "other" +`, + packageName: "fmt", + expected: `package main + +import "strings" +import "fmt" + +type Value int + +import "other" +`, + }, + } + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + lines := strings.Split(test.templContents, "\n") + imp := addImport(lines, test.packageName) + textWithoutNewline := strings.TrimSuffix(imp.Text, "\n") + actualLines := append(lines[:imp.InsertAtLine], append([]string{textWithoutNewline}, lines[imp.InsertAtLine:]...)...) + actual := strings.Join(actualLines, "\n") + if diff := cmp.Diff(test.expected, actual); diff != "" { + t.Error(diff) + } + }) + } +} diff --git a/cmd/templ/lspcmd/proxy/server.go b/cmd/templ/lspcmd/proxy/server.go index 3f43bd1b0..adfc13bba 100644 --- a/cmd/templ/lspcmd/proxy/server.go +++ b/cmd/templ/lspcmd/proxy/server.go @@ -3,6 +3,7 @@ package proxy import ( "context" "fmt" + "regexp" "strings" "github.com/a-h/parse" @@ -343,19 +344,13 @@ func (p *Server) Completion(ctx context.Context, params *lsp.CompletionParams) ( if !ok { continue } - insertAtLine, multiLineImport, hasOtherImports := findLastImport(doc) + imp := addImport(doc.Lines, item.Label) te := lsp.TextEdit{ Range: lsp.Range{ - Start: lsp.Position{Line: uint32(insertAtLine), Character: 0}, - End: lsp.Position{Line: uint32(insertAtLine), Character: 0}, + Start: lsp.Position{Line: uint32(imp.InsertAtLine), Character: 0}, + End: lsp.Position{Line: uint32(imp.InsertAtLine), Character: 0}, }, - NewText: fmt.Sprintf("import \"%s\"", item.Label), - } - if multiLineImport { - te.NewText = fmt.Sprintf("\t\"%s\"\n", item.Label) - } - if !hasOtherImports { - te.NewText = "\n" + te.NewText + "\n" + NewText: imp.Text, } item.AdditionalTextEdits = []lsp.TextEdit{te} } @@ -364,29 +359,45 @@ func (p *Server) Completion(ctx context.Context, params *lsp.CompletionParams) ( return } -func findLastImport(document *Document) (insertAtLine int, multiLineImport bool, hasOtherImports bool) { - latestSingleLineImport := 1 - var line string - for insertAtLine, line = range document.Lines { +type importInsert struct { + InsertAtLine int + Text string +} + +var nonImportKeywordRegexp = regexp.MustCompile(`^(?:templ|func|css|script|var|const|type)\s`) + +func addImport(lines []string, pkg string) (result importInsert) { + var isInMultiLineImport bool + lastSingleLineImportIndex := -1 + for lineIndex, line := range lines { if strings.HasPrefix(line, "import (") { - multiLineImport = true - hasOtherImports = true + isInMultiLineImport = true continue } if strings.HasPrefix(line, "import \"") { - latestSingleLineImport = insertAtLine - hasOtherImports = true + lastSingleLineImportIndex = lineIndex continue } - if multiLineImport && strings.HasPrefix(line, ")") { - return + if isInMultiLineImport && strings.HasPrefix(line, ")") { + return importInsert{ + InsertAtLine: lineIndex, + Text: fmt.Sprintf("\t%q\n", pkg), + } } // Only add import statements before templates, functions, css, and script templates. - if strings.HasPrefix(line, "templ ") || strings.HasPrefix(line, "func ") || strings.HasPrefix(line, "css ") || strings.HasPrefix(line, "script ") { + if nonImportKeywordRegexp.MatchString(line) { break } } - return latestSingleLineImport, false, hasOtherImports + var suffix string + if lastSingleLineImportIndex == -1 { + lastSingleLineImportIndex = 1 + suffix = "\n" + } + return importInsert{ + InsertAtLine: lastSingleLineImportIndex + 1, + Text: fmt.Sprintf("import %q\n%s", pkg, suffix), + } } func (p *Server) CompletionResolve(ctx context.Context, params *lsp.CompletionItem) (result *lsp.CompletionItem, err error) { From 3f70cbccbeac09d4ad27d5b37a4c9bfc1e7835cd Mon Sep 17 00:00:00 2001 From: Adrian Hesketh Date: Mon, 4 Sep 2023 10:12:20 +0100 Subject: [PATCH 4/7] refactor: rename field --- cmd/templ/lspcmd/proxy/import_test.go | 2 +- cmd/templ/lspcmd/proxy/server.go | 16 ++++++++-------- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/cmd/templ/lspcmd/proxy/import_test.go b/cmd/templ/lspcmd/proxy/import_test.go index 8011aafb0..73f6b62d1 100644 --- a/cmd/templ/lspcmd/proxy/import_test.go +++ b/cmd/templ/lspcmd/proxy/import_test.go @@ -254,7 +254,7 @@ import "other" lines := strings.Split(test.templContents, "\n") imp := addImport(lines, test.packageName) textWithoutNewline := strings.TrimSuffix(imp.Text, "\n") - actualLines := append(lines[:imp.InsertAtLine], append([]string{textWithoutNewline}, lines[imp.InsertAtLine:]...)...) + actualLines := append(lines[:imp.LineIndex], append([]string{textWithoutNewline}, lines[imp.LineIndex:]...)...) actual := strings.Join(actualLines, "\n") if diff := cmp.Diff(test.expected, actual); diff != "" { t.Error(diff) diff --git a/cmd/templ/lspcmd/proxy/server.go b/cmd/templ/lspcmd/proxy/server.go index adfc13bba..7cb4fc025 100644 --- a/cmd/templ/lspcmd/proxy/server.go +++ b/cmd/templ/lspcmd/proxy/server.go @@ -347,8 +347,8 @@ func (p *Server) Completion(ctx context.Context, params *lsp.CompletionParams) ( imp := addImport(doc.Lines, item.Label) te := lsp.TextEdit{ Range: lsp.Range{ - Start: lsp.Position{Line: uint32(imp.InsertAtLine), Character: 0}, - End: lsp.Position{Line: uint32(imp.InsertAtLine), Character: 0}, + Start: lsp.Position{Line: uint32(imp.LineIndex), Character: 0}, + End: lsp.Position{Line: uint32(imp.LineIndex), Character: 0}, }, NewText: imp.Text, } @@ -360,8 +360,8 @@ func (p *Server) Completion(ctx context.Context, params *lsp.CompletionParams) ( } type importInsert struct { - InsertAtLine int - Text string + LineIndex int + Text string } var nonImportKeywordRegexp = regexp.MustCompile(`^(?:templ|func|css|script|var|const|type)\s`) @@ -380,8 +380,8 @@ func addImport(lines []string, pkg string) (result importInsert) { } if isInMultiLineImport && strings.HasPrefix(line, ")") { return importInsert{ - InsertAtLine: lineIndex, - Text: fmt.Sprintf("\t%q\n", pkg), + LineIndex: lineIndex, + Text: fmt.Sprintf("\t%q\n", pkg), } } // Only add import statements before templates, functions, css, and script templates. @@ -395,8 +395,8 @@ func addImport(lines []string, pkg string) (result importInsert) { suffix = "\n" } return importInsert{ - InsertAtLine: lastSingleLineImportIndex + 1, - Text: fmt.Sprintf("import %q\n%s", pkg, suffix), + LineIndex: lastSingleLineImportIndex + 1, + Text: fmt.Sprintf("import %q\n%s", pkg, suffix), } } From 01bbf021e1fba9eb079da1a463299518299e17c0 Mon Sep 17 00:00:00 2001 From: Adrian Hesketh Date: Mon, 4 Sep 2023 10:25:34 +0100 Subject: [PATCH 5/7] fix: use the detail section, because the label only includes the last part of the name --- cmd/templ/lspcmd/proxy/import_test.go | 3 ++- cmd/templ/lspcmd/proxy/server.go | 6 +++--- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/cmd/templ/lspcmd/proxy/import_test.go b/cmd/templ/lspcmd/proxy/import_test.go index 73f6b62d1..85ec2368a 100644 --- a/cmd/templ/lspcmd/proxy/import_test.go +++ b/cmd/templ/lspcmd/proxy/import_test.go @@ -1,6 +1,7 @@ package proxy import ( + "fmt" "strings" "testing" @@ -252,7 +253,7 @@ import "other" for _, test := range tests { t.Run(test.name, func(t *testing.T) { lines := strings.Split(test.templContents, "\n") - imp := addImport(lines, test.packageName) + imp := addImport(lines, fmt.Sprintf("%q", test.packageName)) textWithoutNewline := strings.TrimSuffix(imp.Text, "\n") actualLines := append(lines[:imp.LineIndex], append([]string{textWithoutNewline}, lines[imp.LineIndex:]...)...) actual := strings.Join(actualLines, "\n") diff --git a/cmd/templ/lspcmd/proxy/server.go b/cmd/templ/lspcmd/proxy/server.go index 7cb4fc025..5dab464a0 100644 --- a/cmd/templ/lspcmd/proxy/server.go +++ b/cmd/templ/lspcmd/proxy/server.go @@ -344,7 +344,7 @@ func (p *Server) Completion(ctx context.Context, params *lsp.CompletionParams) ( if !ok { continue } - imp := addImport(doc.Lines, item.Label) + imp := addImport(doc.Lines, item.Detail) te := lsp.TextEdit{ Range: lsp.Range{ Start: lsp.Position{Line: uint32(imp.LineIndex), Character: 0}, @@ -381,7 +381,7 @@ func addImport(lines []string, pkg string) (result importInsert) { if isInMultiLineImport && strings.HasPrefix(line, ")") { return importInsert{ LineIndex: lineIndex, - Text: fmt.Sprintf("\t%q\n", pkg), + Text: fmt.Sprintf("\t%s\n", pkg), } } // Only add import statements before templates, functions, css, and script templates. @@ -396,7 +396,7 @@ func addImport(lines []string, pkg string) (result importInsert) { } return importInsert{ LineIndex: lastSingleLineImportIndex + 1, - Text: fmt.Sprintf("import %q\n%s", pkg, suffix), + Text: fmt.Sprintf("import %s\n%s", pkg, suffix), } } From 6a6e2f0fd3ecf414adbb05f6da3d32c99e109e12 Mon Sep 17 00:00:00 2001 From: Adrian Galbenus Date: Tue, 5 Sep 2023 15:49:43 +0300 Subject: [PATCH 6/7] fix: solve auto-import for completions that require them (#138) * fixed autoimport for other completion kinds that require import * Apply suggestions from code review remove unused parameter Co-authored-by: Adrian Hesketh * Apply suggestions from code review fix naming typo Co-authored-by: Adrian Hesketh * changed how a kind that requires import is identified * remove obsolete regexp --------- Co-authored-by: agalbenus Co-authored-by: Adrian Hesketh --- cmd/templ/lspcmd/proxy/server.go | 35 ++++++++++++++++++++++++-------- 1 file changed, 27 insertions(+), 8 deletions(-) diff --git a/cmd/templ/lspcmd/proxy/server.go b/cmd/templ/lspcmd/proxy/server.go index 5dab464a0..10edcf0ad 100644 --- a/cmd/templ/lspcmd/proxy/server.go +++ b/cmd/templ/lspcmd/proxy/server.go @@ -307,6 +307,9 @@ func (p *Server) ColorPresentation(ctx context.Context, params *lsp.ColorPresent return } +var pkgFromImportDetail = regexp.MustCompile(`"([^"]+)"`) +var completionWithImport = regexp.MustCompile(`^.*(\(from\s".+"\))$`) + func (p *Server) Completion(ctx context.Context, params *lsp.CompletionParams) (result *lsp.CompletionList, err error) { p.Log.Info("client -> server: Completion") defer p.Log.Info("client -> server: Completion end") @@ -344,21 +347,37 @@ func (p *Server) Completion(ctx context.Context, params *lsp.CompletionParams) ( if !ok { continue } - imp := addImport(doc.Lines, item.Detail) - te := lsp.TextEdit{ - Range: lsp.Range{ - Start: lsp.Position{Line: uint32(imp.LineIndex), Character: 0}, - End: lsp.Position{Line: uint32(imp.LineIndex), Character: 0}, - }, - NewText: imp.Text, - } + te := handleImportTextEdit(doc, item.Detail) item.AdditionalTextEdits = []lsp.TextEdit{te} + } else { + m := completionWithImport.FindStringSubmatch(item.Detail) + if m != nil { + doc, ok := p.TemplSource.Get(string(templURI)) + if !ok { + continue + } + pkg := pkgFromImportDetail.FindStringSubmatch(m[1])[0] + te := handleImportTextEdit(doc, pkg) + item.AdditionalTextEdits = []lsp.TextEdit{te} + } } result.Items[i] = item } return } +func handleImportTextEdit(doc *Document, pkg string) lsp.TextEdit { + imp := addImport(doc.Lines, pkg) + te := lsp.TextEdit{ + Range: lsp.Range{ + Start: lsp.Position{Line: uint32(imp.LineIndex), Character: 0}, + End: lsp.Position{Line: uint32(imp.LineIndex), Character: 0}, + }, + NewText: imp.Text, + } + return te +} + type importInsert struct { LineIndex int Text string From ce27e21c923a96c0ec101fead186a15993a3d6f9 Mon Sep 17 00:00:00 2001 From: Adrian Hesketh Date: Tue, 5 Sep 2023 15:57:03 +0100 Subject: [PATCH 7/7] refactor: minor restructure, and additional test --- cmd/templ/lspcmd/proxy/import_test.go | 28 ++++++++++++++++++ cmd/templ/lspcmd/proxy/server.go | 42 +++++++++++---------------- 2 files changed, 45 insertions(+), 25 deletions(-) diff --git a/cmd/templ/lspcmd/proxy/import_test.go b/cmd/templ/lspcmd/proxy/import_test.go index 85ec2368a..c31f17573 100644 --- a/cmd/templ/lspcmd/proxy/import_test.go +++ b/cmd/templ/lspcmd/proxy/import_test.go @@ -263,3 +263,31 @@ import "other" }) } } + +func TestGetPackageFromItemDetail(t *testing.T) { + tests := []struct { + input string + expected string + }{ + { + input: `"fmt"`, + expected: `"fmt"`, + }, + { + input: `func(state fmt.State, verb rune) string (from "fmt")`, + expected: `"fmt"`, + }, + { + input: `non matching`, + expected: `non matching`, + }, + } + for _, test := range tests { + t.Run(test.input, func(t *testing.T) { + actual := getPackageFromItemDetail(test.input) + if test.expected != actual { + t.Errorf("expected %q, got %q", test.expected, actual) + } + }) + } +} diff --git a/cmd/templ/lspcmd/proxy/server.go b/cmd/templ/lspcmd/proxy/server.go index 10edcf0ad..68b4c6766 100644 --- a/cmd/templ/lspcmd/proxy/server.go +++ b/cmd/templ/lspcmd/proxy/server.go @@ -307,9 +307,6 @@ func (p *Server) ColorPresentation(ctx context.Context, params *lsp.ColorPresent return } -var pkgFromImportDetail = regexp.MustCompile(`"([^"]+)"`) -var completionWithImport = regexp.MustCompile(`^.*(\(from\s".+"\))$`) - func (p *Server) Completion(ctx context.Context, params *lsp.CompletionParams) (result *lsp.CompletionList, err error) { p.Log.Info("client -> server: Completion") defer p.Log.Info("client -> server: Completion end") @@ -342,23 +339,21 @@ func (p *Server) Completion(ctx context.Context, params *lsp.CompletionParams) ( if item.TextEdit != nil { item.TextEdit.Range = p.convertGoRangeToTemplRange(templURI, item.TextEdit.Range) } - if item.Kind == lsp.CompletionItemKindModule && len(item.AdditionalTextEdits) > 0 { + if len(item.AdditionalTextEdits) > 0 { doc, ok := p.TemplSource.Get(string(templURI)) if !ok { continue } - te := handleImportTextEdit(doc, item.Detail) - item.AdditionalTextEdits = []lsp.TextEdit{te} - } else { - m := completionWithImport.FindStringSubmatch(item.Detail) - if m != nil { - doc, ok := p.TemplSource.Get(string(templURI)) - if !ok { - continue - } - pkg := pkgFromImportDetail.FindStringSubmatch(m[1])[0] - te := handleImportTextEdit(doc, pkg) - item.AdditionalTextEdits = []lsp.TextEdit{te} + pkg := getPackageFromItemDetail(item.Detail) + imp := addImport(doc.Lines, pkg) + item.AdditionalTextEdits = []lsp.TextEdit{ + { + Range: lsp.Range{ + Start: lsp.Position{Line: uint32(imp.LineIndex), Character: 0}, + End: lsp.Position{Line: uint32(imp.LineIndex), Character: 0}, + }, + NewText: imp.Text, + }, } } result.Items[i] = item @@ -366,16 +361,13 @@ func (p *Server) Completion(ctx context.Context, params *lsp.CompletionParams) ( return } -func handleImportTextEdit(doc *Document, pkg string) lsp.TextEdit { - imp := addImport(doc.Lines, pkg) - te := lsp.TextEdit{ - Range: lsp.Range{ - Start: lsp.Position{Line: uint32(imp.LineIndex), Character: 0}, - End: lsp.Position{Line: uint32(imp.LineIndex), Character: 0}, - }, - NewText: imp.Text, +var completionWithImport = regexp.MustCompile(`^.*\(from\s(".+")\)$`) + +func getPackageFromItemDetail(pkg string) string { + if m := completionWithImport.FindStringSubmatch(pkg); len(m) == 2 { + return m[1] } - return te + return pkg } type importInsert struct {