fatih / vim-go

Go development plugin for Vim
https://www.patreon.com/bhcleek
Other
16.01k stars 1.45k forks source link

Go/golang auto-indentation not working as expected: left aligns in some cases #2769

Closed IAXES closed 4 years ago

IAXES commented 4 years ago

Pre-amble:

I'm not 100% certain if this is an issue within vim-go or vim in general, given somewhat similar posts I've read on here (can't find the exact ones at the moment) that imply an issue in vim specifically. Presumably vim-go overrides vim's default behavior for Go/golang buffers, so I'll start here.

What did you do?

I setup a Docker test image (instructions plus files will be included at the bottom of this submission), and am using it to write/debug Go/golang code. If I, for example, write in code that represents a function call, and I manually insert a newline between the parentheses (i.e. (, ) ) characters, I would normally expect the brackets to align with the beginning of the function name, or otherwise honor the cindent plus cinoptions configuration in my .vimrc, which appear to be honored for other file types (i.e. .c and .h for C, .hpp and .cpp for C++, .py for Python 2/3, etc.).

For example, here's a simple hello-world snippet I'm working on. image

If I were to insert a comma plus newline literal after world" and before the trailing ), I normally expect (due to my cinoptions) for the ) character to align with the opening ( from the line above. If cinoptions isn't specified, I'd expect it to at least align with the first non-tab character of the line above.

Instead, this is what happens:

image

There are lots of other examples I could provide, but it seems the recurring them is that auto-indentation, either when typical code normally, or re-indenting a file (i.e. gg=G) results in auto-indent not being invoked (at least, no as expected), and cinoptions likely also being ignored.

What did you expect to happen?

cinoptions to be honored, or for "prettier" auto-indentation to be generated.

What happened instead?

Error case noted above: auto-indentation does not appear to be working.

Note: I've repeated this test with/without other plugins (i.e. Ale, YCM), and with/without cinoptions being set in my .vimrc, and in all cases, I encountered the exact same error case described at the beginning of this issue/ticket.

Configuration (MUST fill this out):

vim-go version:

HEAD/master (installed via vundle):

commit 52b66c0b125e095bc5a1d54d9810c5a0c8141906 (HEAD -> master, origin/master, origin/HEAD)
Author: Billie Cleek <bhcleek@gmail.com>
Date:   Wed Mar 4 18:55:08 2020 -0800

    update CHANGELOG.md for #2753

vimrc you used to reproduce:

vimrc
" Initial settings for Vundle to help manage our plugins.
set nocompatible
filetype off
" set the runtime path to include Vundle and initialize
set rtp+=~/.vim/bundle/Vundle.vim
" Vundle plugins.
call vundle#begin()
" let Vundle manage Vundle, required
Plugin 'gmarik/Vundle.vim'
" Linting and code fixes.
Plugin 'w0rp/ale'
" Code completion.
Plugin 'Valloric/YouCompleteMe'
" Go development plugin.
Plugin 'fatih/vim-go'
call vundle#end()

" Enable filetype-specific plugins and (auto) indentation.
filetype plugin indent on

" Enable syntax highlighting.
syntax on
" Show (partial) command in the last line of the screen.
set showcmd
" Encoding used for the terminal.
set termencoding=utf-8
" Show the line and column number of the cursor position.
set ruler
" While typing a search command, show where the pattern, as it was typed
" so far, matches.
set incsearch ignorecase hlsearch
" Enable visual line numbering.
set number
" Make backspace work line in "other" editors (i.e. can delete a newline).
set backspace=indent,eol,start

" Go/golang specific settings. Works for any programming language other than Go
" at this time.
autocmd BufEnter *.go  setlocal
            \ tabstop=8 shiftwidth=8 softtabstop=8 textwidth=80 noexpandtab
            \ cindent cinoptions=:0,l1,t0,g0,(0,W8
            \ filetype=go

Vim version (first three lines from :version):

VIM - Vi IMproved 8.2 (2019 Dec 12, compiled Mar 13 2020 16:46:48) Included patches: 1-374 Compiled by vim-testbed

Go version (go version):

go version go1.13.4 linux/amd64

Go environment

go env Output:

GO111MODULE=""
GOARCH="amd64"
GOBIN="/usr/lib/go/bin"
GOCACHE="/root/.cache/go-build"
GOENV="/root/.config/go/env"
GOEXE=""
GOFLAGS=""
GOHOSTARCH="amd64"
GOHOSTOS="linux"
GONOPROXY=""
GONOSUMDB=""
GOOS="linux"
GOPATH="/root/workspace"
GOPRIVATE=""
GOPROXY="https://proxy.golang.org,direct"
GOROOT="/usr/lib/go"
GOSUMDB="sum.golang.org"
GOTMPDIR=""
GOTOOLDIR="/usr/lib/go/pkg/tool/linux_amd64"
GCCGO="gccgo"
AR="ar"
CC="gcc"
CXX="g++"
CGO_ENABLED="1"
GOMOD=""
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-build947840206=/tmp/go-build -gno-record-gcc-switches"

gopls version

gopls version Output:

golang.org/x/tools/gopls v0.3.3
    golang.org/x/tools/gopls@v0.3.3 h1:mTFqRDJQmpSsgDDWvbtGnSva1z9uX2XcDszSWa6DhBQ=

vim-go configuration:

vim-go configuration
g:go_jump_to_error = 1
g:go_loaded_gosnippets = 1
g:go_loaded_install = 1

filetype detection configuration:

filetype detection
filetype detection:ON  plugin:ON  indent:ON

Dockerfile:

FROM testbed/vim:latest

# Trigger build process provided by original author of "testbed/vim" (thanks for
# writing this!).
RUN install_vim -tag v8.2.0374 -name vim-test-bin -py3 -build -install

# Add deps.
RUN apk update
RUN apk add                 \
        bash                \
        git                 \
        go                  \
        python3             \
        python3-dev         \
        cmake               \
        alpine-sdk
# Needed by YCM ==> clang.
RUN apk add --no-cache libc6-compat

# Setup Go.
RUN mkdir -p /root/go
ENV GOROOT="/usr/lib/go"
ENV GOBIN="$GOROOT/bin"
ENV GOPATH="/root/workspace"
ENV PATH="$PATH:$GOBIN:$GOPATH/bin"

# Add our test vimrc files.
ADD vimrc  /root/.vimrc

# Install Vundle package manager.
RUN mkdir -p /root/.vim/bundle
RUN mkdir -p /root/.vim/ftplugin
RUN git clone https://github.com/VundleVim/Vundle.vim.git /root/.vim/bundle/Vundle.vim

# Run vim and trigger the installation of the plugins we're interested in
# validating.
RUN /vim-build/bin/vim-test-bin                 \
        -c PluginInstall                        \
        -c 'qa!'

# Trigger YCM build.
RUN cd /root/.vim/bundle/YouCompleteMe/ && python3 ./install.py --go-completer

# Install Go-related binaries for vim.
RUN /vim-build/bin/vim-test-bin                 \
        -c GoInstallBinaries                    \
        -c 'qa!'

# Create a symlink to our test binary.
RUN ln -s /vim-build/bin/vim-test-bin /usr/local/bin/vim

# Deploy hello-world source example.
# ADD hello.go /root/

# Start the container, and run:
# vim /root/hello.go
bhcleek commented 4 years ago

vim-go's indenting rules are in indent/go.vim. You can see there that vim-go uses indentexpr, because (as explained in indent/go.vim) the cindent rules don't work well for Go.

If you want to submit a fix, I'm happy to evaluate. I'll submit that this isn't a high priority item worth worrying about too much, tough, because gofmt makes this not much of an issue generally.

IAXES commented 4 years ago

@bhcleek Is there a setting I could add to my .vimrc file to have the indentation/alignment/etc. rules implemented by vim-go "applied live" (i.e. as-I-type), or is that something that should be handled by another tool/plugin (i.e. to fix code while typing according to the rules provided by gofmt)?

Edit: do the rules in indent/go.vim normally work "live" while a user is typing Go code into a buffer? i.e. is it reasonable to expected some form of auto-indentation to be enabled? Even if cinoptions are ignored, it seems like auto-indentation still isn't working in the general sense.

Thank you.

bhcleek commented 4 years ago

Yes, the indentation defined in indent/go.vim work live. Generally, the rules do work well, but there are some edges. Are you saying that you never see correct indentation?

As for gofmt, it runs when a buffer is saved by default, so any deficiencies in vim-go's indentation will be corrected when the buffer is saved. Are you seeing that?

IAXES commented 4 years ago

@bhcleek Unfortunately, no in both cases.

Yes, the indentation defined in indent/go.vim work live. Generally, the rules do work well, but there are some edges. Are you saying that you never see correct indentation?

Some times I see something aligned to the correct l-brace (i.e. {), but that's about it. I'd say the current setup is maybe tackling 10% of the expected/anticipated use cases.

As for gofmt, it runs when a buffer is saved by default, so any deficiencies in vim-go's indentation will be corrected when the buffer is saved. Are you seeing that?

No, I do not see this being executed either. I've seen some .vimrc suggestions to manually enable it on StackOverflow, but it does not appear that vim-go is executing that operation.

bhcleek commented 4 years ago

Let's get your formatting on save fixed first. It's probably the easiest thing to address, and may shed light on your other issues.

You shouldn't need to enable it explicitly, because it's enabled by default.

What does :echo go#config#FmtAutosave() output?

bhcleek commented 4 years ago

I modified the vimrc that you provided slightly by eliminating other plugins and everything seems to be working as expected as far as I can tell.

Here's the vimrc that I used:

" Initial settings for Vundle to help manage our plugins.
set nocompatible
filetype off
" set the runtime path to include Vundle and initialize
set rtp+=~/.vim/bundle/Vundle.vim
" Vundle plugins.
call vundle#begin()
" let Vundle manage Vundle, required
Plugin 'gmarik/Vundle.vim'
" Go development plugin.
Plugin 'fatih/vim-go'
call vundle#end()
" Enable filetype-specific plugins and (auto) indentation.

filetype plugin indent on

" Enable syntax highlighting.
syntax on

" Show (partial) command in the last line of the screen.
set showcmd

" Encoding used for the terminal.
set termencoding=utf-8

" Show the line and column number of the cursor position.
set ruler

" While typing a search command, show where the pattern, as it was typed
" so far, matches.
set incsearch ignorecase hlsearch

" Enable visual line numbering.
set number

" Make backspace work line in "other" editors (i.e. can delete a newline).
set backspace=indent,eol,start

" Go/golang specific settings. Works for any programming language other than Go
" at this time.
autocmd BufEnter *.go  setlocal
\ tabstop=8 shiftwidth=8 softtabstop=8 textwidth=80 noexpandtab
\ cindent cinoptions=:0,l1,t0,g0,(0,W8
\ filetype=go
Here's the Dockerfile that I used:

FROM testbed/vim:latest

\# Trigger build process provided by original author of "testbed/vim" (thanks for
\# writing this!).
RUN install_vim -tag v8.2.0374 -name vim-test-bin -py3 -build -install

\# Add deps.
RUN apk update
RUN apk add                 \
        bash                \
        git                 \
        go                  \
        python3             \
        python3-dev         \
        cmake               \
        alpine-sdk
\# Needed by YCM ==> clang.
RUN apk add --no-cache libc6-compat

\# Setup Go.
RUN mkdir -p /root/go
ENV GOROOT="/usr/lib/go"
ENV GOBIN="$GOROOT/bin"
ENV GOPATH="/root/workspace"
ENV PATH="$PATH:$GOBIN:$GOPATH/bin"

\# Add our test vimrc files.
ADD vimrc  /root/.vimrc

\# Install Vundle package manager.
RUN mkdir -p /root/.vim/bundle
RUN mkdir -p /root/.vim/ftplugin
RUN git clone https://github.com/VundleVim/Vundle.vim.git /root/.vim/bundle/Vundle.vim

\# Run vim and trigger the installation of the plugins we're interested in
\# validating.
RUN /vim-build/bin/vim-test-bin                 \
        -c PluginInstall                        \
        -c 'qa!'

\# Install Go-related binaries for vim.
RUN /vim-build/bin/vim-test-bin                 \
        -c GoInstallBinaries                    \
        -c 'qa!'

\# Create a symlink to our test binary.
RUN ln -s /vim-build/bin/vim-test-bin /usr/local/bin/vim

\# Deploy hello-world source example.
\# ADD hello.go /root/

\# Start the container, and run:
\# vim /root/hello.go

I then built the docker image, ran it in a container, adding a foo.go file manually. Here's an animated gif of what I saw. Do you experience the same? recording

IAXES commented 4 years ago

Thank you for your help. Working as expected.

bhcleek commented 4 years ago

@IAXES did you figure out what was wrong on your end? If you ended up implementing a fix in your vimrc or something, future readers may be interested to know...

IAXES commented 4 years ago

I stand corrected, and I'll have to re-open. I originally thought the issue was due to me mis-typing, but it seems there bizarre, specific ways in which live-indentation issues can arise. On the plus side, gofmt seems to remedy them when saving a file/buffer, though the live-indentation quirks slow code typing/coding.

To the issue, please see the video (below). There is some really weird "memory" artifact I'm seeing for the line fmt.Println("go-fmt"), among other live indentation quirks.

asciicast

bhcleek commented 4 years ago

What bizarre behavior do you see? Are you referring to the second line that is indented an additional level when saving? That might not be ideal, but bizarre seems a bit strong. Is this the only issue that you're concerned about? As demonstrated previously, generally lines are indented as expected. The exception to that is when within a function or method and the first line contains not just the opening paranthesis, but also an argument. If you don't add an argument and end the line with the opening parenthesis, you'll see that the next line is indented as you expect.

Are there issues here that I'm not recognizing?

IAXES commented 4 years ago

Are you referring to the second line that is indented an additional level when saving? That might not be ideal, but bizarre seems a bit strong.

Pardon for that: I meant to indicate the behavior was unexpected.

Upon re-reviewing these discussions and the points you brought up, the only real issue is the difference between the output of the auto-indenter and the changes gofmt executes upon saving the file/buffer (i.e. "the second line that is indented an additional level when saving"). Is it possible/viable to align these behaviors? If so great, and if not, no further comments/concerns on my part (i.e. ticket/issue can be closed).

Thank you.

bhcleek commented 4 years ago

I don't see a good way to align these two behaviors for the reasons right now. If indent/go.vim were modified to try to detect an unbalanced set of parentheses (i.e more opening than closing), then we'd have problems with string literals. Given that that this indentation has to work off of regular expression/text matching and not a parser, it will never be perfect, and I'd rather stick with the current behavior, because gofmt will correct it, but adding an indentation incorrectly in a multi-line string or even when the previous line has an opening parenthesis without a closing parenthesis in a string literal would be frustrating for users and would not be fixed by gofmt.

I have been considering adding formatting with gopls as the users types, but haven't gotten around to doing that yet. There are several edge cases that have to be handled to make that happen well, and honestly, something other LSP plugins support that well, so I don't see much need yet for vim-go to provide it.

Thanks for engaging quickly and for providing a useful description with an easy way to replicate.