Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

x/tools/gopls: implement rangeFormatting LSP request #31150

Open
myitcv opened this issue Mar 29, 2019 · 21 comments
Open

x/tools/gopls: implement rangeFormatting LSP request #31150

myitcv opened this issue Mar 29, 2019 · 21 comments
Labels
FeatureRequest gopls Issues related to the Go language server, gopls. help wanted
Milestone

Comments

@myitcv
Copy link
Member

myitcv commented Mar 29, 2019

What version of Go are you using (go version)?

$ go version
go version go1.12.4 linux/amd64
$ go list -m golang.org/x/tools
golang.org/x/tools v0.0.0-20190428024724-550556f78a90

Does this issue reproduce with the latest release?

Yes

What operating system and processor architecture are you using (go env)?

go env Output
$ go env
GOARCH="amd64"
GOBIN="/home/myitcv/gostuff/src/github.com/myitcv/govim/.bin"
GOCACHE="/home/myitcv/.cache/go-build"
GOEXE=""
GOFLAGS=""
GOHOSTARCH="amd64"
GOHOSTOS="linux"
GOOS="linux"
GOPATH="/home/myitcv/gostuff"
GOPROXY=""
GORACE=""
GOROOT="/home/myitcv/gos"
GOTMPDIR=""
GOTOOLDIR="/home/myitcv/gos/pkg/tool/linux_amd64"
GCCGO="gccgo"
CC="gcc"
CXX="g++"
CGO_ENABLED="1"
GOMOD="/home/myitcv/gostuff/src/github.com/myitcv/govim/go.mod"
CGO_CFLAGS="-g -O2"
CGO_CPPFLAGS=""
CGO_CXXFLAGS="-g -O2"
CGO_FFLAGS="-g -O2"
CGO_LDFLAGS="-g -O2"
PKG_CONFIG="pkg-config"
GOGCCFLAGS="-fPIC -m64 -pthread -fmessage-length=0 -fdebug-prefix-map=/tmp/go-build120717318=/tmp/go-build -gno-record-gcc-switches"

What did you do?

Considering the following govim testscript in which I attempt to range-format line numbers 3-5 (inc) in main.go:

vim ex 'e main.go'
vim ex '3,5GOVIMGoFmt'
vim ex 'noautocmd w'
cmp main.go main.go.golden

-- go.mod --
module mod

-- main.go --
package main

func main(){
fmt.Println("Hello, world")
}
-- main.go.golden --
package main

func main() {
	fmt.Println("Hello, world")
}

I get the error:

ToUTF16Column: point is missing offset

The params sent were:

&protocol.DocumentRangeFormattingParams{
    TextDocument: protocol.TextDocumentIdentifier{URI:"file://$WORK/main.go"},
    Range:        protocol.Range{
        Start: protocol.Position{Line:2, Character:0},
        End:   protocol.Position{Line:5, Character:0},
    },
    Options: protocol.FormattingOptions{},
}

What did you expect to see?

No error.

What did you see instead?

The above error.

cc @stamblerre @ianthehat

@myitcv myitcv added the gopls Issues related to the Go language server, gopls. label Mar 29, 2019
@gopherbot gopherbot added this to the Unreleased milestone Mar 29, 2019
@sysulq

This comment has been minimized.

@myitcv

This comment has been minimized.

@ianthehat
Copy link

I think I fixed this in https://go-review.googlesource.com/c/tools/+/170186, please re-open if it is still happening.

@myitcv
Copy link
Member Author

myitcv commented Apr 3, 2019

I'm still seeing this in 202502a:

https://travis-ci.org/myitcv/govim/builds/515152204

@myitcv myitcv reopened this Apr 3, 2019
@andybons andybons added the NeedsInvestigation Someone must examine and confirm this is a valid issue and not a duplicate of an existing one. label Apr 3, 2019
@qqqasdwx
Copy link

qqqasdwx commented Apr 4, 2019

I use vscode-go get same error , when I save a go file,I miss result
Output->go-langserver

[Error - 下午2:46:00] Request textDocument/codeAction failed.
  Message: ToUTF16Column: point is missing offset
  Code: 0 

And my settings.json

    "go.alternateTools": {
        "go-langserver": "gopls",
      },
    "go.languageServerExperimentalFeatures": {
        "format": true,
        "autoComplete": true,
        "goToDefinition": true,
        "signatureHelp": true,
        "goToTypeDefinition": true
    },
    "go.useLanguageServer": true,
    "[go]": {
        "editor.snippetSuggestions": "none",
        "editor.formatOnSave": true,
        "editor.codeActionsOnSave": {
            "source.organizeImports": true
        },
    }

I try

go get -u -v golang.org/x/tools/cmd/gopls

it's not ok T.T

@zippoxer
Copy link

zippoxer commented Apr 24, 2019

Can reproduce on Windows 10 64 bit, Go 1.12.4, VSCode 1.33.1 and gopls@cb2dda6eabdf9160e66ca7293897f984154a7f8b (latest at this time).

VSCode settings:

  "go.useLanguageServer": true,
  "[go]": {
    "editor.snippetSuggestions": "none",
    "editor.formatOnSave": true,
    "editor.codeActionsOnSave": {
      "source.organizeImports": true
    }
  },
  "gopls": {
    "usePlaceholders": true,
    "enhancedHover": true
  },

Edit: I noticed this error is showing up every time I save a Go file.

@zippoxer
Copy link

zippoxer commented Apr 26, 2019

I've found a temporary solution: convert all your source files from CRLF to LF (from Windows to Unix line endings).

Note for VSCode users: don't forget to put "files.eol": "\n" in your settings.json, otherwise new files will still use CRLF.

🍡 Yay! 🍡 Now I'm not seeing the ToUTF16Column error anymore, and gopls formats my source files when it didn't before.

Hope this helps some fellow DOSphers.

@myitcv
Copy link
Member Author

myitcv commented Apr 28, 2019

@zippoxer the ToUTF16Column errors are, I think, a separate issue, and should be fixed as of golang/tools@0c752f5

@sleep2death
Copy link

pulled the latest 'tools', vscode with gopls is still not working...

@kylelemons
Copy link
Contributor

I can confirm that my ToUTF16Column errors also went away on Windows with VSCode-Go after resetting my line endings to lf in git.

@myitcv
Copy link
Member Author

myitcv commented May 1, 2019

I'm still seeing this despite not having any Windows line endings.

Sample stack trace (if I change a return to a panic):

panic: ToUTF16Column: point is missing offset

goroutine 5 [running]:
golang.org/x/tools/internal/span.ToUTF16Column(0x2, 0x1, 0xffffffffffffffff, 0xc00d4580d0, 0xd, 0x10, 0x1, 0x0, 0x0)
	/tmp/tmp.xetMpJpdla/golang.org/x/tools/internal/span/utf16.go:25 +0x561
golang.org/x/tools/internal/lsp/protocol.(*ColumnMapper).Position(0xc0010b0450, 0x2, 0x1, 0xffffffffffffffff, 0x0, 0x0, 0x0, 0x0)
	/tmp/tmp.xetMpJpdla/golang.org/x/tools/internal/lsp/protocol/span.go:62 +0x63
golang.org/x/tools/internal/lsp/protocol.(*ColumnMapper).Range(0xc0010b0450, 0xc0028581c0, 0x39, 0x1, 0x1, 0x0, 0x2, 0x1, 0xffffffffffffffff, 0x39, ...)
	/tmp/tmp.xetMpJpdla/golang.org/x/tools/internal/lsp/protocol/span.go:54 +0x4b0
golang.org/x/tools/internal/lsp.ToProtocolEdits(0xc0010b0450, 0xc0005ba320, 0x2, 0x2, 0xc000182740, 0x37ec55, 0x37ec62, 0xc0005ba320, 0x2)
	/tmp/tmp.xetMpJpdla/golang.org/x/tools/internal/lsp/format.go:68 +0x134
golang.org/x/tools/internal/lsp.organizeImports(0x8dff20, 0xc006308740, 0x8e00a0, 0xc0001c8000, 0xc0028581c0, 0x39, 0x1, 0x1, 0x0, 0x1, ...)
	/tmp/tmp.xetMpJpdla/golang.org/x/tools/internal/lsp/code_action.go:86 +0x21e
golang.org/x/tools/internal/lsp.(*Server).codeAction(0xc0000aa7e0, 0x8dff20, 0xc006308740, 0xc003688000, 0xc003688000, 0x0, 0x0, 0x0, 0xc0005ba280)
	/tmp/tmp.xetMpJpdla/golang.org/x/tools/internal/lsp/code_action.go:32 +0x1fb
golang.org/x/tools/internal/lsp.(*Server).CodeAction(0xc0000aa7e0, 0x8dff20, 0xc006308740, 0xc003688000, 0xc003688000, 0x0, 0x0, 0x10, 0x0)
	/tmp/tmp.xetMpJpdla/golang.org/x/tools/internal/lsp/server.go:202 +0x4d
golang.org/x/tools/internal/lsp/protocol.serverHandler.func1(0x8dff20, 0xc006308740, 0xc00014e3f0, 0xc003ebe940)
	/tmp/tmp.xetMpJpdla/golang.org/x/tools/internal/lsp/protocol/server.go:336 +0x2bdd
golang.org/x/tools/internal/jsonrpc2.(*Conn).Run.func1(0xc000074a80, 0xc00014e3f0)
	/tmp/tmp.xetMpJpdla/golang.org/x/tools/internal/jsonrpc2/jsonrpc2.go:276 +0xda
created by golang.org/x/tools/internal/jsonrpc2.(*Conn).Run
	/tmp/tmp.xetMpJpdla/golang.org/x/tools/internal/jsonrpc2/jsonrpc2.go:270 +0xba

@ianthehat - is the related to the diff changes that landed not long ago?

@ianthehat
Copy link

I am confused as to what is happening here, and I can't reproduce it.
For that stack trace to be produced, the span of an edit must have an invalid end position.
It is failing in the call to m.Position(s.End()) a couple of lines after doing s, err := s.WithAll(m.Converter) which means that either that function failed to set the offset without failing, or something concurrently modified the offset

@myitcv
Copy link
Member Author

myitcv commented May 1, 2019

Ok, I've managed to track down this particular instance of the ToUTF16Column: point is missing offset error to the case where a file contains a single line that is the package clause, with no trailing \n. Here is the sequence of events from govim's perspective:

Sequence of events
gopls server start =======================
gopls.Initialize() call; params:
&protocol.InitializeParams{
    ProcessID:    0,
    RootPath:     "",
    RootURI:      "file:///home/myitcv/gostuff/src/github.com/myitcv/playground",
    Capabilities: protocol.ClientCapabilities{
        Workspace:    struct { ApplyEdit bool "json:\"applyEdit,omitempty\""; WorkspaceEdit struct { DocumentChanges bool "json:\"documentChanges,omitempty\""; ResourceOperations []protocol.ResourceOperationKind "json:\"resourceOperations,omitempty\""; FailureHandling protocol.FailureHandlingKind "json:\"failureHandling,omitempty\"" } "json:\"workspaceEdit,omitempty\""; DidChangeConfiguration struct { DynamicRegistration bool "json:\"dynamicRegistration,omitempty\"" } "json:\"didChangeConfiguration,omitempty\""; DidChangeWatchedFiles struct { DynamicRegistration bool "json:\"dynamicRegistration,omitempty\"" } "json:\"didChangeWatchedFiles,omitempty\""; Symbol struct { DynamicRegistration bool "json:\"dynamicRegistration,omitempty\""; SymbolKind struct { ValueSet []protocol.SymbolKind "json:\"valueSet,omitempty\"" } "json:\"symbolKind,omitempty\"" } "json:\"symbol,omitempty\""; ExecuteCommand struct { DynamicRegistration bool "json:\"dynamicRegistration,omitempty\"" } "json:\"executeCommand,omitempty\""; WorkspaceFolders bool "json:\"workspaceFolders,omitempty\""; Configuration bool "json:\"configuration,omitempty\"" }{},
        TextDocument: struct { Synchronization struct { DynamicRegistration bool "json:\"dynamicRegistration,omitempty\""; WillSave bool "json:\"willSave,omitempty\""; WillSaveWaitUntil bool "json:\"willSaveWaitUntil,omitempty\""; DidSave bool "json:\"didSave,omitempty\"" } "json:\"synchronization,omitempty\""; Completion struct { DynamicRegistration bool "json:\"dynamicRegistration,omitempty\""; CompletionItem struct { SnippetSupport bool "json:\"snippetSupport,omitempty\""; CommitCharactersSupport bool "json:\"commitCharactersSupport,omitempty\""; DocumentationFormat []protocol.MarkupKind "json:\"documentationFormat,omitempty\""; DeprecatedSupport bool "json:\"deprecatedSupport,omitempty\""; PreselectSupport bool "json:\"preselectSupport,omitempty\"" } "json:\"completionItem,omitempty\""; CompletionItemKind struct { ValueSet []protocol.CompletionItemKind "json:\"valueSet,omitempty\"" } "json:\"completionItemKind,omitempty\""; ContextSupport bool "json:\"contextSupport,omitempty\"" } "json:\"completion,omitempty\""; Hover struct { DynamicRegistration bool "json:\"dynamicRegistration,omitempty\""; ContentFormat []protocol.MarkupKind "json:\"contentFormat,omitempty\"" } "json:\"hover,omitempty\""; SignatureHelp struct { DynamicRegistration bool "json:\"dynamicRegistration,omitempty\""; SignatureInformation struct { DocumentationFormat []protocol.MarkupKind "json:\"documentationFormat,omitempty\""; ParameterInformation struct { LabelOffsetSupport bool "json:\"labelOffsetSupport,omitempty\"" } "json:\"parameterInformation,omitempty\"" } "json:\"signatureInformation,omitempty\"" } "json:\"signatureHelp,omitempty\""; References struct { DynamicRegistration bool "json:\"dynamicRegistration,omitempty\"" } "json:\"references,omitempty\""; DocumentHighlight struct { DynamicRegistration bool "json:\"dynamicRegistration,omitempty\"" } "json:\"documentHighlight,omitempty\""; DocumentSymbol struct { DynamicRegistration bool "json:\"dynamicRegistration,omitempty\""; SymbolKind struct { ValueSet []protocol.SymbolKind "json:\"valueSet,omitempty\"" } "json:\"symbolKind,omitempty\""; HierarchicalDocumentSymbolSupport bool "json:\"hierarchicalDocumentSymbolSupport,omitempty\"" } "json:\"documentSymbol,omitempty\""; Formatting struct { DynamicRegistration bool "json:\"dynamicRegistration,omitempty\"" } "json:\"formatting,omitempty\""; RangeFormatting struct { DynamicRegistration bool "json:\"dynamicRegistration,omitempty\"" } "json:\"rangeFormatting,omitempty\""; OnTypeFormatting struct { DynamicRegistration bool "json:\"dynamicRegistration,omitempty\"" } "json:\"onTypeFormatting,omitempty\""; Definition struct { DynamicRegistration bool "json:\"dynamicRegistration,omitempty\""; LinkSupport bool "json:\"linkSupport,omitempty\"" } "json:\"definition,omitempty\""; CodeAction struct { DynamicRegistration bool "json:\"dynamicRegistration,omitempty\""; CodeActionLiteralSupport struct { CodeActionKind struct { ValueSet []protocol.CodeActionKind "json:\"valueSet\"" } "json:\"codeActionKind\"" } "json:\"codeActionLiteralSupport,omitempty\"" } "json:\"codeAction,omitempty\""; CodeLens struct { DynamicRegistration bool "json:\"dynamicRegistration,omitempty\"" } "json:\"codeLens,omitempty\""; DocumentLink struct { DynamicRegistration bool "json:\"dynamicRegistration,omitempty\"" } "json:\"documentLink,omitempty\""; Rename struct { DynamicRegistration bool "json:\"dynamicRegistration,omitempty\""; PrepareSupport bool "json:\"prepareSupport,omitempty\"" } "json:\"rename,omitempty\""; PublishDiagnostics struct { RelatedInformation bool "json:\"relatedInformation,omitempty\""; TagSupport bool "json:\"tagSupport,omitempty\"" } "json:\"publishDiagnostics,omitempty\""; Implementation struct { DynamicRegistration bool "json:\"dynamicRegistration,omitempty\""; LinkSupport bool "json:\"linkSupport,omitempty\"" } "json:\"implementation,omitempty\""; TypeDefinition struct { DynamicRegistration bool "json:\"dynamicRegistration,omitempty\""; LinkSupport bool "json:\"linkSupport,omitempty\"" } "json:\"typeDefinition,omitempty\""; ColorProvider struct { DynamicRegistration bool "json:\"dynamicRegistration,omitempty\"" } "json:\"colorProvider,omitempty\""; FoldingRange struct { DynamicRegistration bool "json:\"dynamicRegistration,omitempty\""; RangeLimit float64 "json:\"rangeLimit,omitempty\""; LineFoldingOnly bool "json:\"lineFoldingOnly,omitempty\"" } "json:\"foldingRange,omitempty\""; Declaration struct { DynamicRegistration bool "json:\"dynamicRegistration,omitempty\""; LinkSupport bool "json:\"linkSupport,omitempty\"" } "json:\"declaration,omitempty\""; SelectionRange struct { DynamicRegistration bool "json:\"dynamicRegistration,omitempty\"" } "json:\"selectionRange,omitempty\"" }{
            Synchronization: struct { DynamicRegistration bool "json:\"dynamicRegistration,omitempty\""; WillSave bool "json:\"willSave,omitempty\""; WillSaveWaitUntil bool "json:\"willSaveWaitUntil,omitempty\""; DidSave bool "json:\"didSave,omitempty\"" }{},
            Completion:      struct { DynamicRegistration bool "json:\"dynamicRegistration,omitempty\""; CompletionItem struct { SnippetSupport bool "json:\"snippetSupport,omitempty\""; CommitCharactersSupport bool "json:\"commitCharactersSupport,omitempty\""; DocumentationFormat []protocol.MarkupKind "json:\"documentationFormat,omitempty\""; DeprecatedSupport bool "json:\"deprecatedSupport,omitempty\""; PreselectSupport bool "json:\"preselectSupport,omitempty\"" } "json:\"completionItem,omitempty\""; CompletionItemKind struct { ValueSet []protocol.CompletionItemKind "json:\"valueSet,omitempty\"" } "json:\"completionItemKind,omitempty\""; ContextSupport bool "json:\"contextSupport,omitempty\"" }{},
            Hover:           struct { DynamicRegistration bool "json:\"dynamicRegistration,omitempty\""; ContentFormat []protocol.MarkupKind "json:\"contentFormat,omitempty\"" }{
                DynamicRegistration: false,
                ContentFormat:       {"plaintext"},
            },
            SignatureHelp:      struct { DynamicRegistration bool "json:\"dynamicRegistration,omitempty\""; SignatureInformation struct { DocumentationFormat []protocol.MarkupKind "json:\"documentationFormat,omitempty\""; ParameterInformation struct { LabelOffsetSupport bool "json:\"labelOffsetSupport,omitempty\"" } "json:\"parameterInformation,omitempty\"" } "json:\"signatureInformation,omitempty\"" }{},
            References:         struct { DynamicRegistration bool "json:\"dynamicRegistration,omitempty\"" }{},
            DocumentHighlight:  struct { DynamicRegistration bool "json:\"dynamicRegistration,omitempty\"" }{},
            DocumentSymbol:     struct { DynamicRegistration bool "json:\"dynamicRegistration,omitempty\""; SymbolKind struct { ValueSet []protocol.SymbolKind "json:\"valueSet,omitempty\"" } "json:\"symbolKind,omitempty\""; HierarchicalDocumentSymbolSupport bool "json:\"hierarchicalDocumentSymbolSupport,omitempty\"" }{},
            Formatting:         struct { DynamicRegistration bool "json:\"dynamicRegistration,omitempty\"" }{},
            RangeFormatting:    struct { DynamicRegistration bool "json:\"dynamicRegistration,omitempty\"" }{},
            OnTypeFormatting:   struct { DynamicRegistration bool "json:\"dynamicRegistration,omitempty\"" }{},
            Definition:         struct { DynamicRegistration bool "json:\"dynamicRegistration,omitempty\""; LinkSupport bool "json:\"linkSupport,omitempty\"" }{},
            CodeAction:         struct { DynamicRegistration bool "json:\"dynamicRegistration,omitempty\""; CodeActionLiteralSupport struct { CodeActionKind struct { ValueSet []protocol.CodeActionKind "json:\"valueSet\"" } "json:\"codeActionKind\"" } "json:\"codeActionLiteralSupport,omitempty\"" }{},
            CodeLens:           struct { DynamicRegistration bool "json:\"dynamicRegistration,omitempty\"" }{},
            DocumentLink:       struct { DynamicRegistration bool "json:\"dynamicRegistration,omitempty\"" }{},
            Rename:             struct { DynamicRegistration bool "json:\"dynamicRegistration,omitempty\""; PrepareSupport bool "json:\"prepareSupport,omitempty\"" }{},
            PublishDiagnostics: struct { RelatedInformation bool "json:\"relatedInformation,omitempty\""; TagSupport bool "json:\"tagSupport,omitempty\"" }{},
            Implementation:     struct { DynamicRegistration bool "json:\"dynamicRegistration,omitempty\""; LinkSupport bool "json:\"linkSupport,omitempty\"" }{},
            TypeDefinition:     struct { DynamicRegistration bool "json:\"dynamicRegistration,omitempty\""; LinkSupport bool "json:\"linkSupport,omitempty\"" }{},
            ColorProvider:      struct { DynamicRegistration bool "json:\"dynamicRegistration,omitempty\"" }{},
            FoldingRange:       struct { DynamicRegistration bool "json:\"dynamicRegistration,omitempty\""; RangeLimit float64 "json:\"rangeLimit,omitempty\""; LineFoldingOnly bool "json:\"lineFoldingOnly,omitempty\"" }{},
            Declaration:        struct { DynamicRegistration bool "json:\"dynamicRegistration,omitempty\""; LinkSupport bool "json:\"linkSupport,omitempty\"" }{},
            SelectionRange:     struct { DynamicRegistration bool "json:\"dynamicRegistration,omitempty\"" }{},
        },
        Experimental: nil,
    },
    InitializationOptions: nil,
    Trace:                 "",
    WorkspaceFolders:      nil,
}
gopls server end =======================
gopls server start =======================
gopls.Initialize() return; err: ; res:
&protocol.InitializeResult{
    Capabilities: protocol.ServerCapabilities{
        TextDocumentSync: map[string]interface {}{
            "openClose": bool(true),
            "change":    float64(1),
        },
        HoverProvider:      true,
        CompletionProvider: &protocol.CompletionOptions{
            TriggerCharacters:   {"."},
            AllCommitCharacters: nil,
            ResolveProvider:     false,
        },
        SignatureHelpProvider: &protocol.SignatureHelpOptions{
            TriggerCharacters: {"(", ","},
        },
        DefinitionProvider:               true,
        ReferencesProvider:               false,
        DocumentHighlightProvider:        true,
        DocumentSymbolProvider:           true,
        WorkspaceSymbolProvider:          false,
        CodeActionProvider:               true,
        CodeLensProvider:                 (*protocol.CodeLensOptions)(nil),
        DocumentFormattingProvider:       true,
        DocumentRangeFormattingProvider:  true,
        DocumentOnTypeFormattingProvider: (*struct { FirstTriggerCharacter string "json:\"firstTriggerCharacter\""; MoreTriggerCharacter []string "json:\"moreTriggerCharacter,omitempty\"" })(nil),
        RenameProvider:                   (*protocol.RenameOptions)(nil),
        DocumentLinkProvider:             (*protocol.DocumentLinkOptions)(nil),
        ExecuteCommandProvider:           (*protocol.ExecuteCommandOptions)(nil),
        Experimental:                     nil,
        ImplementationProvider:           false,
        TypeDefinitionProvider:           true,
        Workspace:                        (*struct { WorkspaceFolders *struct { Supported bool "json:\"supported,omitempty\""; ChangeNotifications string "json:\"changeNotifications,omitempty\"" } "json:\"workspaceFolders,omitempty\"" })(nil),
        ColorProvider:                    false,
        FoldingRangeProvider:             false,
        DeclarationProvider:              false,
        SelectionRangeProvider:           false,
    },
    Custom: {},
}
gopls server end =======================
gopls server start =======================
gopls.DidOpen() call; params:
&protocol.DidOpenTextDocumentParams{
    TextDocument: protocol.TextDocumentItem{URI:"file:///home/myitcv/gostuff/src/github.com/myitcv/playground/main.go", LanguageID:"", Version:0, Text:"package main\n"},
}
gopls server end =======================
gopls server start =======================
gopls.DidOpen() return; err: 
gopls server end =======================
gopls server start =======================
gopls.DidOpen() call; params:
&protocol.DidOpenTextDocumentParams{
    TextDocument: protocol.TextDocumentItem{URI:"file:///home/myitcv/gostuff/src/github.com/myitcv/playground/blah.go", LanguageID:"", Version:0, Text:""},
}
gopls server end =======================
gopls server start =======================
gopls.DidOpen() return; err: 
gopls server end =======================
gopls server start =======================
gopls.DidChange() call; params:
&protocol.DidChangeTextDocumentParams{
    TextDocument: protocol.VersionedTextDocumentIdentifier{
        Version:                1,
        TextDocumentIdentifier: protocol.TextDocumentIdentifier{URI:"file:///home/myitcv/gostuff/src/github.com/myitcv/playground/blah.go"},
    },
    ContentChanges: {
        {
            Range:       (*protocol.Range)(nil),
            RangeLength: 0,
            Text:        "p",
        },
    },
}
gopls server end =======================
gopls server start =======================
gopls.DidChange() return; err: 
gopls server end =======================
gopls server start =======================
gopls.DidChange() call; params:
&protocol.DidChangeTextDocumentParams{
    TextDocument: protocol.VersionedTextDocumentIdentifier{
        Version:                2,
        TextDocumentIdentifier: protocol.TextDocumentIdentifier{URI:"file:///home/myitcv/gostuff/src/github.com/myitcv/playground/blah.go"},
    },
    ContentChanges: {
        {
            Range:       (*protocol.Range)(nil),
            RangeLength: 0,
            Text:        "pa",
        },
    },
}
gopls server end =======================
gopls server start =======================
gopls.DidChange() return; err: 
gopls server end =======================
gopls server start =======================
gopls.DidChange() call; params:
&protocol.DidChangeTextDocumentParams{
    TextDocument: protocol.VersionedTextDocumentIdentifier{
        Version:                3,
        TextDocumentIdentifier: protocol.TextDocumentIdentifier{URI:"file:///home/myitcv/gostuff/src/github.com/myitcv/playground/blah.go"},
    },
    ContentChanges: {
        {
            Range:       (*protocol.Range)(nil),
            RangeLength: 0,
            Text:        "pac",
        },
    },
}
gopls server end =======================
gopls server start =======================
gopls.DidChange() return; err: 
gopls server end =======================
gopls server start =======================
gopls.DidChange() call; params:
&protocol.DidChangeTextDocumentParams{
    TextDocument: protocol.VersionedTextDocumentIdentifier{
        Version:                4,
        TextDocumentIdentifier: protocol.TextDocumentIdentifier{URI:"file:///home/myitcv/gostuff/src/github.com/myitcv/playground/blah.go"},
    },
    ContentChanges: {
        {
            Range:       (*protocol.Range)(nil),
            RangeLength: 0,
            Text:        "pack",
        },
    },
}
gopls server end =======================
gopls server start =======================
gopls.DidChange() return; err: 
gopls server end =======================
gopls server start =======================
gopls.DidChange() call; params:
&protocol.DidChangeTextDocumentParams{
    TextDocument: protocol.VersionedTextDocumentIdentifier{
        Version:                5,
        TextDocumentIdentifier: protocol.TextDocumentIdentifier{URI:"file:///home/myitcv/gostuff/src/github.com/myitcv/playground/blah.go"},
    },
    ContentChanges: {
        {
            Range:       (*protocol.Range)(nil),
            RangeLength: 0,
            Text:        "packa",
        },
    },
}
gopls server end =======================
gopls server start =======================
gopls.DidChange() return; err: 
gopls server end =======================
gopls server start =======================
gopls.DidChange() call; params:
&protocol.DidChangeTextDocumentParams{
    TextDocument: protocol.VersionedTextDocumentIdentifier{
        Version:                6,
        TextDocumentIdentifier: protocol.TextDocumentIdentifier{URI:"file:///home/myitcv/gostuff/src/github.com/myitcv/playground/blah.go"},
    },
    ContentChanges: {
        {
            Range:       (*protocol.Range)(nil),
            RangeLength: 0,
            Text:        "packag",
        },
    },
}
gopls server end =======================
gopls server start =======================
gopls.DidChange() return; err: 
gopls server end =======================
gopls server start =======================
gopls.DidChange() call; params:
&protocol.DidChangeTextDocumentParams{
    TextDocument: protocol.VersionedTextDocumentIdentifier{
        Version:                7,
        TextDocumentIdentifier: protocol.TextDocumentIdentifier{URI:"file:///home/myitcv/gostuff/src/github.com/myitcv/playground/blah.go"},
    },
    ContentChanges: {
        {
            Range:       (*protocol.Range)(nil),
            RangeLength: 0,
            Text:        "package",
        },
    },
}
gopls server end =======================
gopls server start =======================
gopls.DidChange() return; err: 
gopls server end =======================
gopls server start =======================
gopls.DidChange() call; params:
&protocol.DidChangeTextDocumentParams{
    TextDocument: protocol.VersionedTextDocumentIdentifier{
        Version:                8,
        TextDocumentIdentifier: protocol.TextDocumentIdentifier{URI:"file:///home/myitcv/gostuff/src/github.com/myitcv/playground/blah.go"},
    },
    ContentChanges: {
        {
            Range:       (*protocol.Range)(nil),
            RangeLength: 0,
            Text:        "package ",
        },
    },
}
gopls server end =======================
gopls server start =======================
gopls.DidChange() return; err: 
gopls server end =======================
gopls server start =======================
gopls.DidChange() call; params:
&protocol.DidChangeTextDocumentParams{
    TextDocument: protocol.VersionedTextDocumentIdentifier{
        Version:                9,
        TextDocumentIdentifier: protocol.TextDocumentIdentifier{URI:"file:///home/myitcv/gostuff/src/github.com/myitcv/playground/blah.go"},
    },
    ContentChanges: {
        {
            Range:       (*protocol.Range)(nil),
            RangeLength: 0,
            Text:        "package m",
        },
    },
}
gopls server end =======================
gopls server start =======================
gopls.DidChange() return; err: 
gopls server end =======================
gopls server start =======================
gopls.DidChange() call; params:
&protocol.DidChangeTextDocumentParams{
    TextDocument: protocol.VersionedTextDocumentIdentifier{
        Version:                10,
        TextDocumentIdentifier: protocol.TextDocumentIdentifier{URI:"file:///home/myitcv/gostuff/src/github.com/myitcv/playground/blah.go"},
    },
    ContentChanges: {
        {
            Range:       (*protocol.Range)(nil),
            RangeLength: 0,
            Text:        "package ma",
        },
    },
}
gopls server end =======================
gopls server start =======================
gopls.DidChange() return; err: 
gopls server end =======================
gopls server start =======================
gopls.DidChange() call; params:
&protocol.DidChangeTextDocumentParams{
    TextDocument: protocol.VersionedTextDocumentIdentifier{
        Version:                11,
        TextDocumentIdentifier: protocol.TextDocumentIdentifier{URI:"file:///home/myitcv/gostuff/src/github.com/myitcv/playground/blah.go"},
    },
    ContentChanges: {
        {
            Range:       (*protocol.Range)(nil),
            RangeLength: 0,
            Text:        "package mai",
        },
    },
}
gopls server end =======================
gopls server start =======================
gopls.DidChange() return; err: 
gopls server end =======================
gopls server start =======================
gopls.DidChange() call; params:
&protocol.DidChangeTextDocumentParams{
    TextDocument: protocol.VersionedTextDocumentIdentifier{
        Version:                12,
        TextDocumentIdentifier: protocol.TextDocumentIdentifier{URI:"file:///home/myitcv/gostuff/src/github.com/myitcv/playground/blah.go"},
    },
    ContentChanges: {
        {
            Range:       (*protocol.Range)(nil),
            RangeLength: 0,
            Text:        "package main",
        },
    },
}
gopls server end =======================
gopls server start =======================
gopls.DidChange() return; err: 
gopls server end =======================
gopls server start =======================
gopls.CodeAction() call; params:
&protocol.CodeActionParams{
    TextDocument: protocol.TextDocumentIdentifier{URI:"file:///home/myitcv/gostuff/src/github.com/myitcv/playground/blah.go"},
    Range:        protocol.Range{},
    Context:      protocol.CodeActionContext{},
}
gopls server end =======================

which gives rise to the following exception:

panic: ToUTF16Column: point is missing offset

goroutine 5 [running]:
golang.org/x/tools/internal/span.ToUTF16Column(0x2, 0x1, 0xffffffffffffffff, 0xc0003c02f0, 0xc, 0x10, 0x1, 0x0, 0x0)
	/tmp/tmp.xetMpJpdla/golang.org/x/tools/internal/span/utf16.go:25 +0x561
golang.org/x/tools/internal/lsp/protocol.(*ColumnMapper).Position(0xc0003fe150, 0x2, 0x1, 0xffffffffffffffff, 0x0, 0x0, 0x0, 0x0)
	/tmp/tmp.xetMpJpdla/golang.org/x/tools/internal/lsp/protocol/span.go:62 +0x63
golang.org/x/tools/internal/lsp/protocol.(*ColumnMapper).Range(0xc0003fe150, 0xc000188460, 0x44, 0x1, 0x1, 0x0, 0x2, 0x1, 0xffffffffffffffff, 0x44, ...)
	/tmp/tmp.xetMpJpdla/golang.org/x/tools/internal/lsp/protocol/span.go:54 +0x4b0
golang.org/x/tools/internal/lsp.ToProtocolEdits(0xc0003fe150, 0xc0003f80a0, 0x2, 0x2, 0xc000182740, 0x16b, 0x177, 0xc0003f80a0, 0x2)
	/tmp/tmp.xetMpJpdla/golang.org/x/tools/internal/lsp/format.go:68 +0x134
golang.org/x/tools/internal/lsp.organizeImports(0x8dff20, 0xc000222b80, 0x8e00a0, 0xc0001c4000, 0xc000188460, 0x44, 0x1, 0x1, 0x0, 0x1, ...)
	/tmp/tmp.xetMpJpdla/golang.org/x/tools/internal/lsp/code_action.go:86 +0x21e
golang.org/x/tools/internal/lsp.(*Server).codeAction(0xc0000a87e0, 0x8dff20, 0xc000222b80, 0xc0003f6000, 0xc0003f6000, 0x0, 0x0, 0x0, 0xc0003f8000)
	/tmp/tmp.xetMpJpdla/golang.org/x/tools/internal/lsp/code_action.go:32 +0x1fb
golang.org/x/tools/internal/lsp.(*Server).CodeAction(0xc0000a87e0, 0x8dff20, 0xc000222b80, 0xc0003f6000, 0xc0003f6000, 0x0, 0x0, 0x0, 0x0)
	/tmp/tmp.xetMpJpdla/golang.org/x/tools/internal/lsp/server.go:202 +0x4d
golang.org/x/tools/internal/lsp/protocol.serverHandler.func1(0x8dff20, 0xc000222b80, 0xc00014c3f0, 0xc000228ec0)
	/tmp/tmp.xetMpJpdla/golang.org/x/tools/internal/lsp/protocol/server.go:336 +0x2bdd
golang.org/x/tools/internal/jsonrpc2.(*Conn).Run.func1(0xc000072a80, 0xc00014c3f0)
	/tmp/tmp.xetMpJpdla/golang.org/x/tools/internal/jsonrpc2/jsonrpc2.go:276 +0xda
created by golang.org/x/tools/internal/jsonrpc2.(*Conn).Run
	/tmp/tmp.xetMpJpdla/golang.org/x/tools/internal/jsonrpc2/jsonrpc2.go:270 +0xba

If instead of simply writing package main to blah.go I write package main\n (i.e. there is a trailing newline), then I don't see any issues.

myitcv added a commit to govim/govim that referenced this issue May 3, 2019
All tests of format-on-save via gofmt and GOVIMGoFmt (remain) skipped
pending golang/go#31759

The tests of range-based GOVIMGoImports and GOVIMGoFmt are skipped
pending golang/go#31150
myitcv added a commit to govim/govim that referenced this issue May 3, 2019
All tests of format-on-save via gofmt and GOVIMGoFmt (remain) skipped
pending golang/go#31759

The tests of range-based GOVIMGoImports and GOVIMGoFmt are skipped
pending golang/go#31150
@iwittkau
Copy link

iwittkau commented May 3, 2019

I had the same issue on a fresh macOS install today (latests Go, VSCode and tools). I was able to resolve the issue by using gofmt -w file.go on the offending file.go. The file contained copied code from json-to-go.

@gopherbot
Copy link

Change https://golang.org/cl/175938 mentions this issue: internal/lsp: disable rangeFormatting temporarily

@stamblerre
Copy link
Contributor

It seems that range formatting has actually not been working for quite some time - temporarily disabling it until we find a good solution. Formatting should still work correctly however.

@stamblerre stamblerre changed the title x/tools/cmd/gopls: support textDocument/rangeFormatting x/tools/gopls: support textDocument/rangeFormatting Jul 2, 2019
@stamblerre stamblerre changed the title x/tools/gopls: support textDocument/rangeFormatting x/tools/gopls: support rangeFormatting LSP request Jul 8, 2019
@stamblerre stamblerre added Suggested Issues that may be good for new contributors looking for work to do. and removed Documentation labels Jul 8, 2019
@stamblerre stamblerre changed the title x/tools/gopls: support rangeFormatting LSP request x/tools/gopls: implement rangeFormatting LSP request Aug 5, 2019
@stamblerre stamblerre added help wanted and removed Suggested Issues that may be good for new contributors looking for work to do. labels Aug 8, 2019
@stamblerre stamblerre modified the milestones: Unreleased, gopls unplanned Dec 4, 2019
@stamblerre stamblerre modified the milestones: gopls unplanned, gopls/v1.0.0 Jan 29, 2020
@stamblerre stamblerre modified the milestones: gopls/v1.0.0, Unreleased Mar 12, 2020
@stamblerre stamblerre added FeatureRequest and removed NeedsInvestigation Someone must examine and confirm this is a valid issue and not a duplicate of an existing one. labels Mar 12, 2020
@stamblerre stamblerre removed this from the Unreleased milestone Jul 23, 2020
@stamblerre stamblerre added this to the gopls/unplanned milestone Oct 21, 2020
@hyangah
Copy link
Contributor

hyangah commented Nov 22, 2021

This is marked as "help wanted" currently. @findleyr @pjweinb According to the description of the change that disabled this feature https://golang.org/cl/175938 I guess the challenge is in using the diff library. Will the new diff work you were looking into remove one of the road blockers?

@pjweinb
Copy link

pjweinb commented Nov 22, 2021

I don't fully understand what was happening, but once the new diff is in, range formatting is a feature we should be able to make work.

@pjweinb
Copy link

pjweinb commented Sep 28, 2022

Go programs are formatted by the go/format package, which has two entry points, format.Node() and format.Source(). The former has restricted input and unless the input is the whole file, loses comments. The latter, to simplify, only formats text that could syntactically be the body of a function. That is, formatting the range
Foo: "xxx",
Bar: "yyy",
to fix the whitespace produces an error. TLDR, neither of the possibilities works adequately well. The alternative, writing our own implementation of formatting, would be error-prone and difficult to keep in sync with go/format. This feature request will be closed soon unless someone has a better idea how to implement it.

@kylelemons
Copy link
Contributor

I'm not super familiar with the LSP API itself, but could you conceivably expand the range you're given until you can format it with Source, and then either return that rewrite or (harder) try to work backwards to figure out the changes required to just the portion that was selected?

@pjweinb
Copy link

pjweinb commented Sep 29, 2022 via email

@pjweinb pjweinb removed their assignment Oct 4, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
FeatureRequest gopls Issues related to the Go language server, gopls. help wanted
Projects
None yet
Development

No branches or pull requests