golang/go

x/tools/gopls: textEdit calculation wrong.

phakepraful opened this issue · 3 comments

gopls version

0.11.0

gopls -v version Build info ---------- golang.org/x/tools/gopls v0.11.0 golang.org/x/tools/gopls@v0.11.0 h1:/nvKHdTtePQmrv9XN3gIUN9MOdUrKzO/dcqgbG6x8EY= github.com/BurntSushi/toml@v1.2.1 h1:9F2/+DoOYIOksmaJFPw1tGFy1eDnIJXg+UHjuD8lTak= github.com/google/go-cmp@v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= github.com/sergi/go-diff@v1.1.0 h1:we8PVUC3FE2uYfodKH/nBHMSetSfHDR6scGdBi+erh0= golang.org/x/exp@v0.0.0-20221031165847-c99f073a8326 h1:QfTh0HpN6hlw6D3vu8DAwC8pBIwikq0AI1evdm+FksE= golang.org/x/exp/typeparams@v0.0.0-20221031165847-c99f073a8326 h1:fl8k2zg28yA23264d82M4dp+YlJ3ngDcpuB1bewkQi4= golang.org/x/mod@v0.7.0 h1:LapD9S96VoQRhi/GrNTqeBJFrUjs5UHCAtTlgwA5oZA= golang.org/x/sync@v0.1.0 h1:wsuoTGHzEhffawBOhz5CYhcrV4IdKZbEyZjBMuTp12o= golang.org/x/sys@v0.2.0 h1:ljd4t30dBnAvMZaQCevtY0xLLD0A+bRZXbgLMLU1F/A= golang.org/x/text@v0.4.0 h1:BrVqGRd7+k1DiOgtnFvAkoQEWQvBc25ouMJM6429SFg= golang.org/x/tools@v0.3.1-0.20221213193459-ca17b2c27ca8 h1:7/HkGkN/2ktghBCSRRgp31wAww4syfsW52tj7yirjWk= golang.org/x/vuln@v0.0.0-20221109205719-3af8368ee4fe h1:qptQiQwEpETwDiz85LKtChqif9xhVkAm8Nhxs0xnTww= honnef.co/go/tools@v0.3.3 h1:oDx7VAwstgpYpb3wv0oxiZlxY+foCpRAwY7Vk6XpAgA= mvdan.cc/gofumpt@v0.4.0 h1:JVf4NN1mIpHogBj7ABpgOyZc65/UUOkKQFkoURsz4MM= mvdan.cc/xurls/v2@v2.4.0 h1:tzxjVAj+wSBmDcF6zBB7/myTy3gX9xvi8Tyr28AuQgc= go: go1.19.2

go env

go env
set GO111MODULE=
set GOARCH=amd64
set GOBIN=
set GOCACHE=C:\Users\x\AppData\Local\go-build
set GOENV=C:\Users\x\AppData\Roaming\go\env
set GOEXE=.exe
set GOEXPERIMENT=
set GOFLAGS=
set GOHOSTARCH=amd64
set GOHOSTOS=windows
set GOINSECURE=
set GOMODCACHE=C:\Users\x\Go\pkg\mod
set GONOPROXY=
set GONOSUMDB=
set GOOS=windows
set GOPATH=C:\Users\x\Go
set GOPRIVATE=
set GOPROXY=https://proxy.golang.org,direct
set GOROOT=H:\Software\Scoop\apps\go\current
set GOSUMDB=sum.golang.org
set GOTMPDIR=
set GOTOOLDIR=H:\Software\Scoop\apps\go\current\pkg\tool\windows_amd64
set GOVCS=
set GOVERSION=go1.19.2
set GCCGO=gccgo
set GOAMD64=v1
set AR=ar
set CC=gcc
set CXX=g++
set CGO_ENABLED=1
set GOMOD=NUL
set GOWORK=
set CGO_CFLAGS=-g -O2
set CGO_CPPFLAGS=
set CGO_CXXFLAGS=-g -O2
set CGO_FFLAGS=-g -O2
set CGO_LDFLAGS=-g -O2
set PKG_CONFIG=pkg-config
set GOGCCFLAGS=-m64 -mthreads -Wl,--no-gc-sections -fmessage-length=0 -fdebug-prefix-map=C:\Users\x\AppData\Local\Temp\go-build3329400193=/tmp/go-build -gno-record-gcc-switches

What did you do?

  1. Create test file.
  2. Declare package, import testing.
  3. Type "func Te"
package something

import "testing"

func Te

206698487-0f6887d2-174a-4d6c-a704-2430d676a565

What did you expect to see?

package something

import "testing"

func TestXxx|(t *testing.T){

}

What did you see instead?

package something

import "testing"

func TeTestXxx|(t *testing.T){

}

Editor and settings

Neovim with nvim-lspconfig. Default settings.

Logs

[Trace - 14:50:54.916 PM] Received response 'textDocument/completion - (117)' in 0ms.
"Result":{
   "isIncomplete":true,
   "items":[
      {
         "label":"TestXxx(t *testing.T)",
         "kind":3,
         "detail":"tab, type the rest of the name, then tab",
         "documentation":"Test test function",
         "preselect":true,
         "sortText":"00000",
         "insertTextFormat":2,
         "textEdit":{
            "range":{
               "start":{
                  "line":4,
                  "character":5
               },
               "end":{
                  "line":4,
                  "character":5
               }
            },
            "newText":"Test${1:Xxx}(t *testing.T) {\n$0\n\\}"
         }
      },
      {
         "label":"TestMain(m *testing.M)",
         "kind":3,
         "documentation":"complete the parameter",
         "sortText":"00001",
         "filterText":"TestMain(m *testing.M)",
         "insertTextFormat":2,
         "textEdit":{
            "range":{
               "start":{
                  "line":4,
                  "character":5
               },
               "end":{
                  "line":4,
                  "character":5
               }
            },
            "newText":"TestMain(m *testing.M)"
         }
      }
   ]
}

gopls.log relevant textEdit at 14:50:54.916 PM

CC @pjweinb

We'll investigate and try to fix this for the next release. I suspect this may have to do with configured completion options / capabilities, as it doesn't seem to affect all clients.

@pjweinb and I looked into this. These edits may be technically correct, based on the LSP spec:
https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#textDocument_completion

There is discussion there of filtering based on word boundaries. In particular, there is the following statement: "If you create a completion item with a text edit at the current cursor position no word guessing takes place and no filtering should happen." But that is not what gopls does: it provides a completion positioned at the start of the Te identifier, and so clients such as VS Code filter these results based on the existing text, and this completion works as expected.

With that said the spec seems a bit vague, and I don't know why we couldn't provide a text edit at the current cursor position, which would be unambigious. We're investigating what this would entail.

Aha, this is a dupe of #56852. Moving discussion over there.