diff --git a/gopls/internal/lsp/source/extract.go b/gopls/internal/lsp/source/extract.go index 9a143a78c2..8e6f77d1e6 100644 --- a/gopls/internal/lsp/source/extract.go +++ b/gopls/internal/lsp/source/extract.go @@ -676,7 +676,7 @@ func adjustRangeForCommentsAndWhiteSpace(rng span.Range, tok *token.File, conten prevStart = start // If start is within a comment, move start to the end // of the comment group. - if file.Comments[startComment].Pos() <= start && start < file.Comments[startComment].End() { + if startComment < len(file.Comments) && file.Comments[startComment].Pos() <= start && start < file.Comments[startComment].End() { start = file.Comments[startComment].End() startComment++ } @@ -697,11 +697,16 @@ func adjustRangeForCommentsAndWhiteSpace(rng span.Range, tok *token.File, conten // Find the index for the first comment that ends after the range end. return file.Comments[i].End() >= rng.End }) + // Search will return n if not found, so we need to adjust if there are no + // comments that would match. + if endComment == len(file.Comments) { + endComment = -1 + } for prevEnd != end { prevEnd = end // If end is within a comment, move end to the start // of the comment group. - if file.Comments[endComment].Pos() < end && end <= file.Comments[endComment].End() { + if endComment >= 0 && file.Comments[endComment].Pos() < end && end <= file.Comments[endComment].End() { end = file.Comments[endComment].Pos() endComment-- } diff --git a/gopls/internal/regtest/misc/extract_test.go b/gopls/internal/regtest/misc/extract_test.go new file mode 100644 index 0000000000..45ddb5408a --- /dev/null +++ b/gopls/internal/regtest/misc/extract_test.go @@ -0,0 +1,69 @@ +// Copyright 2022 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 misc + +import ( + "testing" + + . "golang.org/x/tools/gopls/internal/lsp/regtest" + "golang.org/x/tools/gopls/internal/lsp/tests/compare" + + "golang.org/x/tools/gopls/internal/lsp/protocol" +) + +func TestExtractFunction(t *testing.T) { + const files = ` +-- go.mod -- +module mod.com + +go 1.12 +-- main.go -- +package main + +func Foo() int { + a := 5 + return a +} +` + Run(t, files, func(t *testing.T, env *Env) { + env.OpenFile("main.go") + + start := env.RegexpSearch("main.go", "a := 5").ToProtocolPosition() + end := env.RegexpSearch("main.go", "return a").ToProtocolPosition() + + actions, err := env.Editor.CodeAction(env.Ctx, "main.go", &protocol.Range{Start: start, End: end}, nil) + if err != nil { + t.Fatal(err) + } + + // Find the extract function code action. + var extractFunc *protocol.CodeAction + for _, action := range actions { + if action.Kind == protocol.RefactorExtract && action.Title == "Extract function" { + extractFunc = &action + break + } + } + if extractFunc == nil { + t.Fatal("could not find extract function action") + } + + env.ApplyCodeAction(*extractFunc) + want := `package main + +func Foo() int { + a := newFunction() + return a +} + +func newFunction() int { + a := 5 + return a +} +` + if got := env.Editor.BufferText("main.go"); got != want { + t.Fatalf("TestFillStruct failed:\n%s", compare.Text(want, got)) + } + }) +}