[go: up one dir, main page]

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

LSP de-sync when using softtabstop=-1 with foldmethod=indent #29119

Closed
icholy opened this issue May 31, 2024 · 7 comments · Fixed by #29136
Closed

LSP de-sync when using softtabstop=-1 with foldmethod=indent #29119

icholy opened this issue May 31, 2024 · 7 comments · Fixed by #29136
Labels
bug issues reporting wrong behavior insert-mode
Milestone

Comments

@icholy
Copy link
Contributor
icholy commented May 31, 2024

Problem

When using softtabstop=-1 and foldmethod=indent inserting tab characters can result in a de-sync. This looks very similar to #25092 but I've confirmed it's not a regression.

Steps to reproduce

Repo: https://github.com/icholy/nvim-bug-29119

init.lua

vim.o.foldmethod = "indent"
vim.o.softtabstop = -1

vim.api.nvim_create_autocmd("FileType", {
        pattern = "go",
        callback = function()
		vim.lsp.start({ cmd = { "gopls" } })
        end
})

main.go

package main

func main() {
	println()
}

Steps:

  1. nvim --clean -u init.lua main.go
  2. Insert <TAB> character right before println() call.
  3. Leave insert mode with <ESC>.

See:

image

Expected behavior

No de-sync

Neovim version (nvim -v)

NVIM v0.11.0-dev-139+ga18652ed6

Vim (not Nvim) behaves the same?

n/a

Operating system/version

Ubuntu 22.04.4 LTS

Terminal name/version

alacritty 0.12.2

$TERM environment variable

alacritty

Installation

bob

@icholy icholy added the bug issues reporting wrong behavior label May 31, 2024
@github-actions github-actions bot added the lsp label May 31, 2024
@wookayin
Copy link
Member
wookayin commented May 31, 2024

Please refrain from using external repositories as a minimal reproduction step (which might be ephemeral!) It'd be great if this issue can be self-contained. The code and reproduction step doesn't seem too long.

Also this might be an issue specific to gopls. Do you happen to experience the same bug with other LSPs?

@icholy
Copy link
Contributor Author
icholy commented May 31, 2024

@wookayin the issue description contains the exact same contents as the repo I linked.

@icholy
Copy link
Contributor Author
icholy commented May 31, 2024

@wookayin I'm able to reproduce with luals as well. I generally use gopls for repros because it's fast to initialize & less buggy than other LSPs in general (at least in my experience).

@icholy
Copy link
Contributor Author
icholy commented Jun 2, 2024

Here's the LSP log:

[START][2024-06-02 00:50:08] LSP logging initiated
[INFO][2024-06-02 00:50:08] .../vim/lsp/rpc.lua:731	"Starting RPC client"	{  cmd = { "gopls" },  extra = {}}
[DEBUG][2024-06-02 00:50:08] .../vim/lsp/rpc.lua:286	"rpc.send"	{  id = 1,  jsonrpc = "2.0",  method = "initialize",  params = {    capabilities = {      general = {        positionEncodings = { "utf-16" }      },      textDocument = {        callHierarchy = {          dynamicRegistration = false        },        codeAction = {          codeActionLiteralSupport = {            codeActionKind = {              valueSet = { "", "quickfix", "refactor", "refactor.extract", "refactor.inline", "refactor.rewrite", "source", "source.organizeImports" }            }          },          dataSupport = true,          dynamicRegistration = true,          isPreferredSupport = true,          resolveSupport = {            properties = { "edit" }          }        },        completion = {          completionItem = {            commitCharactersSupport = false,            deprecatedSupport = false,            documentationFormat = { "markdown", "plaintext" },            preselectSupport = false,            resolveSupport = {              properties = { "additionalTextEdits" }            },            snippetSupport = true          },          completionItemKind = {            valueSet = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25 }          },          completionList = {            itemDefaults = { "editRange", "insertTextFormat", "insertTextMode", "data" }          },          contextSupport = false,          dynamicRegistration = false        },        declaration = {          linkSupport = true        },        definition = {          dynamicRegistration = true,          linkSupport = true        },        diagnostic = {          dynamicRegistration = false        },        documentHighlight = {          dynamicRegistration = false        },        documentSymbol = {          dynamicRegistration = false,          hierarchicalDocumentSymbolSupport = true,          symbolKind = {            valueSet = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26 }          }        },        formatting = {          dynamicRegistration = true        },        hover = {          contentFormat = { "markdown", "plaintext" },          dynamicRegistration = true        },        implementation = {          linkSupport = true        },        inlayHint = {          dynamicRegistration = true,          resolveSupport = {            properties = { "textEdits", "tooltip", "location", "command" }          }        },        publishDiagnostics = {          dataSupport = true,          relatedInformation = true,          tagSupport = {            valueSet = { 1, 2 }          }        },        rangeFormatting = {          dynamicRegistration = true        },        references = {          dynamicRegistration = false        },        rename = {          dynamicRegistration = true,          prepareSupport = true        },        semanticTokens = {          augmentsSyntaxTokens = true,          dynamicRegistration = false,          formats = { "relative" },          multilineTokenSupport = false,          overlappingTokenSupport = true,          requests = {            full = {              delta = true            },            range = false          },          serverCancelSupport = false,          tokenModifiers = { "declaration", "definition", "readonly", "static", "deprecated", "abstract", "async", "modification", "documentation", "defaultLibrary" },          tokenTypes = { "namespace", "type", "class", "enum", "interface", "struct", "typeParameter", "parameter", "variable", "property", "enumMember", "event", "function", "method", "macro", "keyword", "modifier", "comment", "string", "number", "regexp", "operator", "decorator" }        },        signatureHelp = {          dynamicRegistration = false,          signatureInformation = {            activeParameterSupport = true,            documentationFormat = { "markdown", "plaintext" },            parameterInformation = {              labelOffsetSupport = true            }          }        },        synchronization = {          didSave = true,          dynamicRegistration = false,          willSave = true,          willSaveWaitUntil = true        },        typeDefinition = {          linkSupport = true        }      },      window = {        showDocument = {          support = true        },        showMessage = {          messageActionItem = {            additionalPropertiesSupport = false          }        },        workDoneProgress = true      },      workspace = {        applyEdit = true,        configuration = true,        didChangeConfiguration = {          dynamicRegistration = false        },        didChangeWatchedFiles = {          dynamicRegistration = false,          relativePatternSupport = true        },        inlayHint = {          refreshSupport = true        },        semanticTokens = {          refreshSupport = true        },        symbol = {          dynamicRegistration = false,          symbolKind = {            valueSet = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26 }          }        },        workspaceEdit = {          resourceOperations = { "rename", "create", "delete" }        },        workspaceFolders = true      }    },    clientInfo = {      name = "Neovim",      version = "0.11.0-dev+ga18652ed6"    },    processId = 16424,    rootPath = vim.NIL,    rootUri = vim.NIL,    trace = "off",    workDoneToken = "1",    workspaceFolders = vim.NIL  }}
[DEBUG][2024-06-02 00:50:08] .../vim/lsp/rpc.lua:408	"rpc.receive"	{  id = 1,  jsonrpc = "2.0",  result = {    capabilities = {      callHierarchyProvider = true,      codeActionProvider = {        codeActionKinds = { "quickfix", "refactor.extract", "refactor.inline", "refactor.rewrite", "source.fixAll", "source.organizeImports" },        resolveProvider = true      },      codeLensProvider = vim.empty_dict(),      completionProvider = {        triggerCharacters = { "." }      },      definitionProvider = true,      documentFormattingProvider = true,      documentHighlightProvider = true,      documentLinkProvider = vim.empty_dict(),      documentSymbolProvider = true,      executeCommandProvider = {        commands = { "gopls.add_dependency", "gopls.add_import", "gopls.add_telemetry_counters", "gopls.apply_fix", "gopls.change_signature", "gopls.check_upgrades", "gopls.diagnose_files", "gopls.edit_go_directive", "gopls.fetch_vulncheck_result", "gopls.gc_details", "gopls.generate", "gopls.go_get_package", "gopls.list_imports", "gopls.list_known_packages", "gopls.maybe_prompt_for_telemetry", "gopls.mem_stats", "gopls.regenerate_cgo", "gopls.remove_dependency", "gopls.reset_go_mod_diagnostics", "gopls.run_go_work_command", "gopls.run_govulncheck", "gopls.run_tests", "gopls.start_debugging", "gopls.start_profile", "gopls.stop_profile", "gopls.test", "gopls.tidy", "gopls.toggle_gc_details", "gopls.update_go_sum", "gopls.upgrade_dependency", "gopls.vendor", "gopls.views", "gopls.workspace_stats" }      },      foldingRangeProvider = true,      hoverProvider = true,      implementationProvider = true,      inlayHintProvider = vim.empty_dict(),      referencesProvider = true,      renameProvider = {        prepareProvider = true      },      selectionRangeProvider = true,      semanticTokensProvider = {        full = true,        legend = {          tokenModifiers = { "declaration", "definition", "readonly", "static", "deprecated", "abstract", "async", "modification", "documentation", "defaultLibrary" },          tokenTypes = { "namespace", "type", "class", "enum", "interface", "struct", "typeParameter", "parameter", "variable", "property", "enumMember", "event", "function", "method", "macro", "keyword", "modifier", "comment", "string", "number", "regexp", "operator", "decorator" }        },        range = true      },      signatureHelpProvider = {        triggerCharacters = { "(", "," }      },      textDocumentSync = {        change = 2,        openClose = true,        save = vim.empty_dict()      },      typeDefinitionProvider = true,      workspace = {        workspaceFolders = {          changeNotifications = "workspace/didChangeWorkspaceFolders",          supported = true        }      },      workspaceSymbolProvider = true    },    serverInfo = {      name = "gopls",      version = '{"GoVersion":"go1.22.0","Path":"golang.org/x/tools/gopls","Main":{"Path":"golang.org/x/tools/gopls","Version":"v0.15.3","Sum":"h1:zbdOidFrPTc8Bx0YrN5QKgJ0zCjyGi0L27sKQ/bDG5o=","Replace":null},"Deps":[{"Path":"github.com/BurntSushi/toml","Version":"v1.2.1","Sum":"h1:9F2/+DoOYIOksmaJFPw1tGFy1eDnIJXg+UHjuD8lTak=","Replace":null},{"Path":"github.com/google/go-cmp","Version":"v0.6.0","Sum":"h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=","Replace":null},{"Path":"golang.org/x/exp/typeparams","Version":"v0.0.0-20221212164502-fae10dda9338","Sum":"h1:2O2DON6y3XMJiQRAS1UWU+54aec2uopH3x7MAiqGW6Y=","Replace":null},{"Path":"golang.org/x/mod","Version":"v0.15.0","Sum":"h1:SernR4v+D55NyBH2QiEQrlBAnj1ECL6AGrA5+dPaMY8=","Replace":null},{"Path":"golang.org/x/sync","Version":"v0.6.0","Sum":"h1:5BMeUDZ7vkXGfEr1x9B4bRcTH4lpkTkpdh0T/J+qjbQ=","Replace":null},{"Path":"golang.org/x/telemetry","Version":"v0.0.0-20240209200032-7b892fcb8a78","Sum":"h1:vcVnuftN4J4UKLRcgetjzfU9FjjgXUUYUc3JhFplgV4=","Replace":null},{"Path":"golang.org/x/text","Version":"v0.14.0","Sum":"h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=","Replace":null},{"Path":"golang.org/x/tools","Version":"v0.18.1-0.20240412183611-d92ae0781217","Sum":"h1:uH9jJYgeLCvblH0S+03kFO0qUDxRkbLRLFiKVVDl7ak=","Replace":null},{"Path":"golang.org/x/vuln","Version":"v1.0.1","Sum":"h1:KUas02EjQK5LTuIx1OylBQdKKZ9jeugs+HiqO5HormU=","Replace":null},{"Path":"honnef.co/go/tools","Version":"v0.4.6","Sum":"h1:oFEHCKeID7to/3autwsWfnuv69j3NsfcXbvJKuIcep8=","Replace":null},{"Path":"mvdan.cc/gofumpt","Version":"v0.6.0","Sum":"h1:G3QvahNDmpD+Aek/bNOLrFR2XC6ZAdo62dZu65gmwGo=","Replace":null},{"Path":"mvdan.cc/xurls/v2","Version":"v2.5.0","Sum":"h1:lyBNOm8Wo71UknhUs4QTFUNNMyxy2JEIaKKo0RWOh+8=","Replace":null}],"Settings":[{"Key":"-buildmode","Value":"exe"},{"Key":"-compiler","Value":"gc"},{"Key":"DefaultGODEBUG","Value":"httplaxcontentlength=1,httpmuxgo121=1,panicnil=1,tls10server=1,tlsrsakex=1,tlsunsafeekm=1"},{"Key":"CGO_ENABLED","Value":"1"},{"Key":"CGO_CFLAGS","Value":""},{"Key":"CGO_CPPFLAGS","Value":""},{"Key":"CGO_CXXFLAGS","Value":""},{"Key":"CGO_LDFLAGS","Value":""},{"Key":"GOARCH","Value":"amd64"},{"Key":"GOOS","Value":"linux"},{"Key":"GOAMD64","Value":"v1"}],"Version":"v0.15.3"}'    }  }}
[DEBUG][2024-06-02 00:50:08] .../vim/lsp/rpc.lua:286	"rpc.send"	{  jsonrpc = "2.0",  method = "initialized",  params = vim.empty_dict()}
[DEBUG][2024-06-02 00:50:08] .../vim/lsp/rpc.lua:286	"rpc.send"	{  jsonrpc = "2.0",  method = "textDocument/didOpen",  params = {    textDocument = {      languageId = "go",      text = "package main\n\nfunc main() {\nprintln()\n}\n\n",      uri = "file:///home/icholy/src/github.com/icholy/nvim-bug-8/main.go",      version = 3    }  }}
[INFO][2024-06-02 00:50:08] ...m/lsp/client.lua:620	"LSP[gopls]"	"server_capabilities"	{  server_capabilities = {    callHierarchyProvider = true,    codeActionProvider = {      codeActionKinds = { "quickfix", "refactor.extract", "refactor.inline", "refactor.rewrite", "source.fixAll", "source.organizeImports" },      resolveProvider = true    },    codeLensProvider = vim.empty_dict(),    completionProvider = {      triggerCharacters = { "." }    },    definitionProvider = true,    documentFormattingProvider = true,    documentHighlightProvider = true,    documentLinkProvider = vim.empty_dict(),    documentSymbolProvider = true,    executeCommandProvider = {      commands = { "gopls.add_dependency", "gopls.add_import", "gopls.add_telemetry_counters", "gopls.apply_fix", "gopls.change_signature", "gopls.check_upgrades", "gopls.diagnose_files", "gopls.edit_go_directive", "gopls.fetch_vulncheck_result", "gopls.gc_details", "gopls.generate", "gopls.go_get_package", "gopls.list_imports", "gopls.list_known_packages", "gopls.maybe_prompt_for_telemetry", "gopls.mem_stats", "gopls.regenerate_cgo", "gopls.remove_dependency", "gopls.reset_go_mod_diagnostics", "gopls.run_go_work_command", "gopls.run_govulncheck", "gopls.run_tests", "gopls.start_debugging", "gopls.start_profile", "gopls.stop_profile", "gopls.test", "gopls.tidy", "gopls.toggle_gc_details", "gopls.update_go_sum", "gopls.upgrade_dependency", "gopls.vendor", "gopls.views", "gopls.workspace_stats" }    },    foldingRangeProvider = true,    hoverProvider = true,    implementationProvider = true,    inlayHintProvider = vim.empty_dict(),    referencesProvider = true,    renameProvider = {      prepareProvider = true    },    selectionRangeProvider = true,    semanticTokensProvider = {      full = true,      legend = {        tokenModifiers = { "declaration", "definition", "readonly", "static", "deprecated", "abstract", "async", "modification", "documentation", "defaultLibrary" },        tokenTypes = { "namespace", "type", "class", "enum", "interface", "struct", "typeParameter", "parameter", "variable", "property", "enumMember", "event", "function", "method", "macro", "keyword", "modifier", "comment", "string", "number", "regexp", "operator", "decorator" }      },      range = true    },    signatureHelpProvider = {      triggerCharacters = { "(", "," }    },    textDocumentSync = {      change = 2,      openClose = true,      save = vim.empty_dict()    },    typeDefinitionProvider = true,    workspace = {      workspaceFolders = {        changeNotifications = "workspace/didChangeWorkspaceFolders",        supported = true      }    },    workspaceSymbolProvider = true  }}
[DEBUG][2024-06-02 00:50:08] .../vim/lsp/rpc.lua:408	"rpc.receive"	{  id = 1,  jsonrpc = "2.0",  method = "window/workDoneProgress/create",  params = {    token = "744700557183600534"  }}
[DEBUG][2024-06-02 00:50:08] ...m/lsp/client.lua:678	"LSP[gopls]"	"client.request"	1	"textDocument/semanticTokens/full"	{  textDocument = {    uri = "file:///home/icholy/src/github.com/icholy/nvim-bug-8/main.go"  }}	<function 1>	1
[DEBUG][2024-06-02 00:50:08] .../vim/lsp/rpc.lua:286	"rpc.send"	{  id = 2,  jsonrpc = "2.0",  method = "textDocument/semanticTokens/full",  params = {    textDocument = {      uri = "file:///home/icholy/src/github.com/icholy/nvim-bug-8/main.go"    }  }}
[DEBUG][2024-06-02 00:50:08] .../vim/lsp/rpc.lua:423	"server_request: callback result"	{  result = vim.NIL,  status = true}
[DEBUG][2024-06-02 00:50:08] .../vim/lsp/rpc.lua:286	"rpc.send"	{  id = 1,  jsonrpc = "2.0",  result = vim.NIL}
[DEBUG][2024-06-02 00:50:08] .../vim/lsp/rpc.lua:408	"rpc.receive"	{  jsonrpc = "2.0",  method = "$/progress",  params = {    token = "744700557183600534",    value = {      kind = "begin",      message = "Loading packages...",      title = "Setting up workspace"    }  }}
[DEBUG][2024-06-02 00:50:08] .../vim/lsp/rpc.lua:408	"rpc.receive"	{  id = 2,  jsonrpc = "2.0",  method = "workspace/configuration",  params = {    items = { {        scopeUri = "file:///home/icholy/src/github.com/icholy/nvim-bug-8",        section = "gopls"      } }  }}
[DEBUG][2024-06-02 00:50:08] .../vim/lsp/rpc.lua:423	"server_request: callback result"	{  result = { vim.NIL },  status = true}
[DEBUG][2024-06-02 00:50:08] .../vim/lsp/rpc.lua:286	"rpc.send"	{  id = 2,  jsonrpc = "2.0",  result = { vim.NIL }}
[DEBUG][2024-06-02 00:50:08] .../vim/lsp/rpc.lua:408	"rpc.receive"	{  jsonrpc = "2.0",  method = "window/logMessage",  params = {    message = "2024/06/02 00:50:08 go info for /home/icholy/src/github.com/icholy/nvim-bug-8\n(view type AdHocView)\n(root dir /home/icholy/src/github.com/icholy/nvim-bug-8)\n(go version go version go1.22.0 linux/amd64)\n(build flags: [])\n(go env: {GOOS:linux GOARCH:amd64 GOCACHE:/home/icholy/.cache/go-build GOMODCACHE:/home/icholy/go/pkg/mod GOPATH:/home/icholy/go GOPRIVATE: GOFLAGS: GO111MODULE: GoVersion:22 GoVersionOutput:go version go1.22.0 linux/amd64\n GOWORK: GOPACKAGESDRIVER:})\n(env overlay: map[])\n\n",    type = 3  }}
[INFO][2024-06-02 00:50:08] ...lsp/handlers.lua:628	"2024/06/02 00:50:08 go info for /home/icholy/src/github.com/icholy/nvim-bug-8\n(view type AdHocView)\n(root dir /home/icholy/src/github.com/icholy/nvim-bug-8)\n(go version go version go1.22.0 linux/amd64)\n(build flags: [])\n(go env: {GOOS:linux GOARCH:amd64 GOCACHE:/home/icholy/.cache/go-build GOMODCACHE:/home/icholy/go/pkg/mod GOPATH:/home/icholy/go GOPRIVATE: GOFLAGS: GO111MODULE: GoVersion:22 GoVersionOutput:go version go1.22.0 linux/amd64\n GOWORK: GOPACKAGESDRIVER:})\n(env overlay: map[])\n\n"
[DEBUG][2024-06-02 00:50:08] .../vim/lsp/rpc.lua:408	"rpc.receive"	{  jsonrpc = "2.0",  method = "window/logMessage",  params = {    message = "2024/06/02 00:50:08 go/packages.Load #1\n\tsnapshot=0\n\tdirectory=file:///home/icholy/src/github.com/icholy/nvim-bug-8\n\tquery=[./ builtin]\n\tpackages=2\n",    type = 3  }}
[INFO][2024-06-02 00:50:08] ...lsp/handlers.lua:628	"2024/06/02 00:50:08 go/packages.Load #1\n\tsnapshot=0\n\tdirectory=file:///home/icholy/src/github.com/icholy/nvim-bug-8\n\tquery=[./ builtin]\n\tpackages=2\n"
[DEBUG][2024-06-02 00:50:08] .../vim/lsp/rpc.lua:408	"rpc.receive"	{  jsonrpc = "2.0",  method = "window/logMessage",  params = {    message = "2024/06/02 00:50:08 go/packages.Load #1: updating metadata for 1 packages\n",    type = 3  }}
[DEBUG][2024-06-02 00:50:08] .../vim/lsp/rpc.lua:408	"rpc.receive"	{  jsonrpc = "2.0",  method = "$/progress",  params = {    token = "744700557183600534",    value = {      kind = "end",      message = "Finished loading packages."    }  }}
[INFO][2024-06-02 00:50:08] ...lsp/handlers.lua:628	"2024/06/02 00:50:08 go/packages.Load #1: updating metadata for 1 packages\n"
[DEBUG][2024-06-02 00:50:08] .../vim/lsp/rpc.lua:408	"rpc.receive"	{  error = {    code = 0,    message = "semantictokens are disabled"  },  id = 2,  jsonrpc = "2.0"}
[DEBUG][2024-06-02 00:50:08] .../vim/lsp/rpc.lua:408	"rpc.receive"	{  jsonrpc = "2.0",  method = "textDocument/publishDiagnostics",  params = {    diagnostics = {},    uri = "file:///home/icholy/src/github.com/icholy/nvim-bug-8/main.go",    version = 3  }}
[DEBUG][2024-06-02 00:50:11] .../vim/lsp/rpc.lua:286	"rpc.send"	{  jsonrpc = "2.0",  method = "textDocument/didChange",  params = {    contentChanges = { {        range = {          ["end"] = {            character = 0,            line = 3          },          start = {            character = 0,            line = 3          }        },        rangeLength = 0,        text = " "      }, {        range = {          ["end"] = {            character = 1,            line = 3          },          start = {            character = 1,            line = 3          }        },        rangeLength = 0,        text = " "      }, {        range = {          ["end"] = {            character = 2,            line = 3          },          start = {            character = 2,            line = 3          }        },        rangeLength = 0,        text = " "      }, {        range = {          ["end"] = {            character = 3,            line = 3          },          start = {            character = 3,            line = 3          }        },        rangeLength = 0,        text = " "      }, {        range = {          ["end"] = {            character = 4,            line = 3          },          start = {            character = 4,            line = 3          }        },        rangeLength = 0,        text = " "      }, {        range = {          ["end"] = {            character = 5,            line = 3          },          start = {            character = 5,            line = 3          }        },        rangeLength = 0,        text = " "      }, {        range = {          ["end"] = {            character = 6,            line = 3          },          start = {            character = 6,            line = 3          }        },        rangeLength = 0,        text = " "      }, {        range = {          ["end"] = {            character = 7,            line = 3          },          start = {            character = 7,            line = 3          }        },        rangeLength = 0,        text = " "      }, {        range = {          ["end"] = {            character = 11,            line = 3          },          start = {            character = 0,            line = 3          }        },        rangeLength = 11,        text = "\tprintln()\0"      } },    textDocument = {      uri = "file:///home/icholy/src/github.com/icholy/nvim-bug-8/main.go",      version = 12    }  }}
[DEBUG][2024-06-02 00:50:11] .../vim/lsp/rpc.lua:408	"rpc.receive"	{  jsonrpc = "2.0",  method = "textDocument/publishDiagnostics",  params = {    diagnostics = { {        message = "expected ';', found 'ILLEGAL'",        range = {          ["end"] = {            character = 10,            line = 3          },          start = {            character = 10,            line = 3          }        },        severity = 1,        source = "syntax"      } },    uri = "file:///home/icholy/src/github.com/icholy/nvim-bug-8/main.go",    version = 12  }}
[DEBUG][2024-06-02 00:50:11] .../vim/lsp/rpc.lua:286	"rpc.send"	{  jsonrpc = "2.0",  method = "$/cancelRequest",  params = {    id = 2  }}
[DEBUG][2024-06-02 00:50:11] ...m/lsp/client.lua:678	"LSP[gopls]"	"client.request"	1	"textDocument/semanticTokens/full"	{  textDocument = {    uri = "file:///home/icholy/src/github.com/icholy/nvim-bug-8/main.go"  }}	<function 1>	1
[DEBUG][2024-06-02 00:50:11] .../vim/lsp/rpc.lua:286	"rpc.send"	{  id = 3,  jsonrpc = "2.0",  method = "textDocument/semanticTokens/full",  params = {    textDocument = {      uri = "file:///home/icholy/src/github.com/icholy/nvim-bug-8/main.go"    }  }}
[DEBUG][2024-06-02 00:50:11] .../vim/lsp/rpc.lua:408	"rpc.receive"	{  error = {    code = 0,    message = "semantictokens are disabled"  },  id = 3,  jsonrpc = "2.0"}

The textDocument/didChange is including a null terminator for some reason "\tprintln()\0"

{
    jsonrpc = "2.0",
    method = "textDocument/didChange",
    params = {
        contentChanges = {
            {
                range = {["end"] = {character = 0, line = 3}, start = {character = 0, line = 3}},
                rangeLength = 0,
                text = " "
            },
            {
                range = {["end"] = {character = 1, line = 3}, start = {character = 1, line = 3}},
                rangeLength = 0,
                text = " "
            },
            {
                range = {["end"] = {character = 2, line = 3}, start = {character = 2, line = 3}},
                rangeLength = 0,
                text = " "
            },
            {
                range = {["end"] = {character = 3, line = 3}, start = {character = 3, line = 3}},
                rangeLength = 0,
                text = " "
            },
            {
                range = {["end"] = {character = 4, line = 3}, start = {character = 4, line = 3}},
                rangeLength = 0,
                text = " "
            },
            {
                range = {["end"] = {character = 5, line = 3}, start = {character = 5, line = 3}},
                rangeLength = 0,
                text = " "
            },
            {
                range = {["end"] = {character = 6, line = 3}, start = {character = 6, line = 3}},
                rangeLength = 0,
                text = " "
            },
            {
                range = {["end"] = {character = 7, line = 3}, start = {character = 7, line = 3}},
                rangeLength = 0,
                text = " "
            },
            {
                range = {["end"] = {character = 11, line = 3}, start = {character = 0, line = 3}},
                rangeLength = 11,
                text = "\tprintln()\0"
            }
        },
        textDocument = {uri = "file:///home/icholy/src/github.com/icholy/nvim-bug-8/main.go", version = 12}
    }
}

@icholy
Copy link
Contributor Author
icholy commented Jun 2, 2024

The on_lines ranges are correct, it's nvim_buf_lines is returning bad data in the on_lines callback (putting it in a vim.schedule works correctly):

init.lua

vim.o.foldmethod = "indent"
vim.o.softtabstop = -1

vim.api.nvim_create_autocmd("BufEnter", {
    callback = function()
        vim.api.nvim_buf_attach(0, false, {
            on_lines = function (_, bufnr, _, firstline, _, new_lastline)
                local lines = vim.api.nvim_buf_get_lines(bufnr, firstline, new_lastline, true) 
                vim.print(lines)
            end
        })
    end
})

test.txt

a
b

Steps:

  • nvim --clean -u init.lua test.txt
  • i<TAB>
  • :messages

Output:

{ " a" }
{ "  a" }
{ "   a" }
{ "    a" }
{ "     a" }
{ "      a" }
{ "       a" }
{ "        a" }
{ "\ta\0     a" }

@icholy
Copy link
Contributor Author
icholy commented Jun 2, 2024

The ml_get_buf_len function is returning the wrong value

size_t bufstrlen = (size_t)ml_get_buf_len(buf, lnum);

The bufstr is \ta but the bufstrlen is 3 which (incorrectly) includes the null terminator. The incorrect buf->b_ml.ml_line_len value is being set here:

buf->b_ml.ml_line_len = (colnr_T)(end - start);

Hardware watchpoint 2: -location buf->b_ml.ml_line_len

Old value = 4
New value = 2
0x0000560d9197021a in ml_get_buf_impl (buf=0x560d931046b0, lnum=1, will_change=false) at /home/icholy/src/github.com/neovim/neovim/src/nvim/memline.c:1941
1941        buf->b_ml.ml_line_len = (colnr_T)(end - start);
(gdb) bt
#0  0x0000560d9197021a in ml_get_buf_impl (buf=0x560d931046b0, lnum=1, will_change=false) at /home/icholy/src/github.com/neovim/neovim/src/nvim/memline.c:1941
#1  0x0000560d9196feb3 in ml_get_buf (buf=0x560d931046b0, lnum=1) at /home/icholy/src/github.com/neovim/neovim/src/nvim/memline.c:1820
#2  0x0000560d919e3326 in plines_win_nofold (wp=0x560d931019d0, lnum=1) at /home/icholy/src/github.com/neovim/neovim/src/nvim/plines.c:768
#3  0x0000560d918d07cc in checkSmall (wp=0x560d931019d0, fp=0x560d931a3790, lnum_off=0) at /home/icholy/src/github.com/neovim/neovim/src/nvim/fold.c:1550
#4  0x0000560d918d072a in check_closed (wp=0x560d931019d0, fp=0x560d931a3790, use_levelp=0x7ffeb379af1e, level=0, maybe_smallp=0x7ffeb379af1d, lnum_off=0) at /home/icholy/src/github.com/neovim/neovim/src/nvim/fold.c:1524
#5  0x0000560d918cdc43 in hasFoldingWin (win=0x560d931019d0, lnum=1, firstp=0x7ffeb379af74, lastp=0x0, cache=false, infop=0x0) at /home/icholy/src/github.com/neovim/neovim/src/nvim/fold.c:226
#6  0x0000560d917e0a91 in changed_common (buf=0x560d931046b0, lnum=1, col=0, lnume=2, xtra=0) at /home/icholy/src/github.com/neovim/neovim/src/nvim/change.c:361
#7  0x0000560d917e0dce in changed_bytes (lnum=1, col=0) at /home/icholy/src/github.com/neovim/neovim/src/nvim/change.c:435
#8  0x0000560d917e0f23 in inserted_bytes (lnum=1, start_col=0, old_col=2, new_col=1) at /home/icholy/src/github.com/neovim/neovim/src/nvim/change.c:471
#9  0x0000560d918254f0 in ins_tab () at /home/icholy/src/github.com/neovim/neovim/src/nvim/edit.c:4433
#10 0x0000560d9181d7da in insert_handle_key (s=0x7ffeb379b490) at /home/icholy/src/github.com/neovim/neovim/src/nvim/edit.c:1036
#11 0x0000560d9181cc59 in insert_execute (state=0x7ffeb379b490, key=9) at /home/icholy/src/github.com/neovim/neovim/src/nvim/edit.c:697
#12 0x0000560d91a78e8e in state_enter (s=0x7ffeb379b490) at /home/icholy/src/github.com/neovim/neovim/src/nvim/state.c:101
#13 0x0000560d9181c153 in insert_enter (s=0x7ffeb379b490) at /home/icholy/src/github.com/neovim/neovim/src/nvim/edit.c:346
#14 0x0000560d9181de28 in edit (cmdchar=105, startln=false, count=1) at /home/icholy/src/github.com/neovim/neovim/src/nvim/edit.c:1296
#15 0x0000560d919a2bbe in invoke_edit (cap=0x7ffeb379b680, repl=0, cmd=105, startln=0) at /home/icholy/src/github.com/neovim/neovim/src/nvim/normal.c:6258
#16 0x0000560d919a2b12 in nv_edit (cap=0x7ffeb379b680) at /home/icholy/src/github.com/neovim/neovim/src/nvim/normal.c:6230
#17 0x0000560d919970eb in normal_execute (state=0x7ffeb379b610, key=105) at /home/icholy/src/github.com/neovim/neovim/src/nvim/normal.c:1230
#18 0x0000560d91a78e8e in state_enter (s=0x7ffeb379b610) at /home/icholy/src/github.com/neovim/neovim/src/nvim/state.c:101
#19 0x0000560d91995317 in normal_enter (cmdwin=false, noexmode=false) at /home/icholy/src/github.com/neovim/neovim/src/nvim/normal.c:519
#20 0x0000560d9193b2db in main (argc=6, argv=0x7ffeb379b9e8) at /home/icholy/src/github.com/neovim/neovim/src/nvim/main.c:665

It's being updated by the folding code. This checks out because foldmethod=indent is required to trigger this bug.

@zeertzjq zeertzjq added insert-mode and removed lsp labels Jun 2, 2024
@zeertzjq
Copy link
Member
zeertzjq commented Jun 2, 2024

This change seems to fix the problem:

diff --git a/src/nvim/edit.c b/src/nvim/edit.c
index 595b4da58..05ad0368f 100644
--- a/src/nvim/edit.c
+++ b/src/nvim/edit.c
@@ -4430,6 +4430,7 @@ static bool ins_tab(void)
         }
         if (!(State & VREPLACE_FLAG)) {
           curbuf->b_ml.ml_line_len -= i;
+          curbuf->b_ml.ml_flags |= ML_LINE_DIRTY;
           inserted_bytes(fpos.lnum, change_col,
                          cursor->col - change_col, fpos.col - change_col);
         }

@zeertzjq zeertzjq added this to the 0.10.1 milestone Jun 2, 2024
zeertzjq added a commit to zeertzjq/neovim that referenced this issue Jun 2, 2024
github-actions bot pushed a commit that referenced this issue Jun 2, 2024
(cherry picked from commit 5633731)
bfredl added a commit that referenced this issue Jul 24, 2024
This is a maintenance release.

FEATURES
--------------------------------------------------------------------------------
- ebc6c38 lsp: update LSP healthcheck format (#28988)

BUILD
--------------------------------------------------------------------------------
- ba90b54 deps: drop unused bundled bash, python parsers and queries
- 803cc08 deps: bump tree-sitter-c to v0.21.3
- 91e337a deps: bump tree-sitter-query to v0.4.0
- 978b63a deps: bump tree-sitter-vimdoc to v3.0.0
- b322c35 "popcount" name conflict on NetBSD #28983
- b397b56 remove -O2 from gen_cflags
- f77db12 reuse code for deps.txt for both deps and main build
- 46d2906 macos: disable deduplication in link step

FIXES
--------------------------------------------------------------------------------
- 70f7708 assert failure in VimL expression parser
- 28f0320 show swapfile warning as a warning (#28972)
- 0d293e4 api: alloc and draw cursor window in nvim__redraw
- e1b6187 colorscheme: underline StatusLineNC with 'notermguicolors' #28810
- aa13218 column: clamp line number for legacy signs
- df6ce83 column: crash with 'signcolumn' set to "number" (#29003)
- 97be9d8 column: modifying a sign should update placed signs (#29750)
- 21b21b9 comment: fall back to using trimmed comment markers (#28950)
- 0827279 defaults: default @/Q broken when 'ignorecase' is set (#29343)
- 5d9f2d7 drawline: don't draw beyond end of window (#29035)
- eadc4e0 drawline: don't draw beyond end of window with 'rnu' (#29406)
- bec397e extmarks: issues with revalidating marks #28961
- 1fe1f85 filetype: fix typos in filetype detection
- ad55ec3 float: handle error in win_float_create() (#29742)
- bf16fe3 fs: make vim.fs.root work for relative paths and unnamed buffers (#28973)
- 5eaae79 health: broken ruby detect #28804
- 24ee2e7 health: fix fetching url with python in provider health (#29594)
- 7582d4a input: handle vim.on_key() properly with ALT and K_SPECIAL (#29677)
- 4f0c4c3 lsp: add textDocument/documentLink to capability map (#28838)
- 2d7aab6 lsp: avoid vim.keymap.del error when stopping a client (#29478)
- bdd5871 lsp: check if buffer was detached in on_init callback (#28942)
- d8ff216 lsp: clear lsp client diagnostics (#29091)
- 3a727be lsp: detach all clients on_reload to force buf_state reload (#28898)
- e98637e lsp: do not detach from buffer if there are uninitialized clients (#29043)
- 0ee3147 lsp: do not reset buf version when detaching client (#29273)
- 24fa65a lsp: don't show codelens for buffers that don't support it (#29690)
- 4efca7c lsp: handle nil root_dir in health check (#29010)
- efe8a0a lsp: hide layout in codelenses in virtual text (#28794) (#28895)
- 2fb69cc lsp: inlay hints are rendered in the correct order (#29707)
- dfff482 lsp: remove superfluous on_detach callback from semantic tokens module (#29188)
- 10a16c1 lsp: trigger LspDetach on buffer delete
- 33121f1 lua: change some vim.fn.expand() to vim.fs.normalize() (#29583)
- ffc457a marks: revalidate marks whose position did not change
- f82d7b8 mouse: early return when clicking in padded 'statuscolumn' (#29394)
- d6756fc move: half-page scrolling with resized grid at eob (#28821)
- 0cf7e25 path: avoid chdir() when resolving path (#28799)
- 63ff733 quickfix: make shortmess+=O work with cmdheight=0 (#29609)
- 46c2962 runtime: add commentstring for glsl ftplugin
- b98aa78 runtime: source c ftplugin properly for cpp on Windows (#29053)
- 039121f snippet: cancel snippet session when leaving the buffer (#29044)
- 84d7bfc snippet: don't override unnamed register on tabstop select (#29008)
- e13f03a snippet: modify base indentation when there's actually whitespace (#29670)
- 9fd6664 tohtml: extmark text may be out of bounds
- 8c88f40 tohtml: ignore lsp inlay hints
- 5cdf0c2 tohtml: properly handle multiple hl groups #29012
- 3d31909 tohtml: replace ipairs with pairs
- 4150e5e tohtml: show how many warnings are hidden
- 0389472 tohtml: support ranges again
- ab2d243 treesitter: display fields for anonymous nodes in :InspectTree
- 35f6425 treesitter: do not modify highlight state for _on_spell_nav
- 356ddb1 treesitter: ensure syntaxset augroup exists (#29542)
- eb53aba treesitter: recognize aliased parsers in omnifunc, query linter
- b6b2272 tui: move $COLORTERM check to _defaults.lua (#29206)
- 89f29fc tui: remove duplicate disabling of synchronized output (#28884)
- a784b90 tui: skip TUI in ui_rgb_attached (#29096)
- 19787d6 ui: avoid ambiguity about last chunk when flushing halfway (#29718)
- 89fa1ee ui: flush ext_cmdline events before doing cmdpreview (#29062)
- 7055cd1 ui: superfluous showmode / excessive grid_cursor_goto #29089
- 6802db7 version: fix vim.version().prerelease
- 728f6c7 vim.text: remove assert from vim.text.hexdecode
- c3aef56 win-msi: add bin to PATH per-machine after installation (#29099)

VIM PATCHES
--------------------------------------------------------------------------------
- fdf769f 0b74eec: runtime(stylus): remove remaining css code (vim/vim#14866)
- 29fd743 74703f1: runtime(doc): remove obsolete Ex insert behavior (#29702)
- 891cc78 7a85e34: runtime(doc): fix inconsistencies in :h file-searching (#29652)
- 704d336 8.2.0083: text properties wrong when tabs and spaces are exchanged
- 571e54e 8.2.0109: corrupted text properties when expanding spaces
- 7c055bd 8.2.3388: fnamemodify('path/..', ':p') differs from using 'path/../' (#29667)
- 9c91233 9.1.0414: Unable to leave long line with 'smoothscroll' and 'scrolloff'
- 34cc49b 9.1.0498: getcmdcompltype() interferes with cmdline completion (#29397)
- 259a620 9.1.0512: Mode message for spell completion doesn't match allowed keys (#29437)
- f89d4ee 9.1.0526: Unwanted cursor movement with pagescroll at start of buffer (#29569)
- 4ce293c 9.1.0565: Stop directory doesn't work properly in 'tags'
- c467bfe 9.1.0566: Stop dir in findfile() doesn't work properly w/o trailing slash
- 576363a 9.1.0567: Cannot use relative paths as findfile() stop directories
- ceb82a9 9.1.0569: fnamemodify() treats ".." and "../" differently (#29673)
- 07de890 9.1.0580: :lmap mapping for keypad key not applied when typed in Select mode (#29693)
- b01202d 9.1.0594: Unnecessary redraw when setting 'winfixbuf' (#29775)
- 804a94d 9.1.0601: Wrong cursor position with 'breakindent' when wide char doesn't fit (#29793)
- a03cc83 df62c62: runtime(doc): grammar fixes in options.txt (#29729)
- fde5718 partial:9.0.0323: using common name in tests leads to flaky tests

REDACTOR
--------------------------------------------------------------------------------
- c35e040 replace deprecated vim.loop with vim.uv
- 3a354bf lsp: reuse buf_detach_client logic in on_detach (#28939) (#29024)
- dffadc3 path.c: add nonnull attributes (#28829)
- db65017 tests: more global highlight definitions
- 3725db6 tests: use more global highlight definitions

CI
--------------------------------------------------------------------------------
- 7400f9d adjust workflows to enable required checks
- 575136c always add `target:release` label when backporting
- 410f43c bump backport action to version 3
- 9a2760a change label `backport` to `target:release`
- b94b341 run workflows on release branches
- 0e81c62 skip lintcommit workflow on release branches

TESTING
--------------------------------------------------------------------------------
- f033484 add a test for #29119
- 18a36d3 do not set termguicolors in test runner
- 68513c2 fix reporting "no flush received" too early (#29735)
- 981548b remove checks for failed tests on Windows
- 6577612 starting and stopping treesitter highlight (#29546)

DOCUMENTATION
--------------------------------------------------------------------------------
- 891b235 document 'list' behavior when 'listchars' excludes "tab" (#29360)
- 8c00651 fix more treesitter parsing errors
- 777e15f update LSP quickstart (#28989)
- 6136326 lpeg: merge upstream changes
- f7d8650 lsp: format the handwritten part #29295
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug issues reporting wrong behavior insert-mode
Projects
None yet
Development

Successfully merging a pull request may close this issue.

3 participants