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

ParseError for textDocument/codeAction with 1-line file #2211

Open
ben-krieger opened this issue Mar 4, 2025 · 3 comments
Open

ParseError for textDocument/codeAction with 1-line file #2211

ben-krieger opened this issue Mar 4, 2025 · 3 comments
Labels
bug Something isn't working

Comments

@ben-krieger
Copy link
Contributor

Zig Version

0.13.0

ZLS Version

0.13.0

Client / Code Editor / Extensions

neovim 10.4 using zls via nvim-lspconfig

Steps to Reproduce and Observed Behavior

In neovim 10.4, using zls via nvim-lspconfig, write a file containing only:

const std = @import("std");

With the BufWritePre callback set to:

local params = vim.lsp.util.make_range_params()
params.context = { only = { "source.organizeImports" } }
-- buf_request_sync defaults to a 1000ms timeout. Depending on your
-- machine and codebase, you may want longer. Add an additional
-- argument after params if you find that you have to write the file
-- twice for changes to be saved.
-- E.g., vim.lsp.buf_request_sync(0, "textDocument/codeAction", params, 3000)
if client.supports_method("textDocument/codeAction") then
	local result = vim.lsp.buf_request_sync(0, "textDocument/codeAction", params)
	for cid, res in pairs(result or {}) do
		for _, r in pairs(res.result or {}) do
			if r.edit then
				local enc = (vim.lsp.get_client_by_id(cid) or {}).offset_encoding or "utf-16"
				vim.lsp.util.apply_workspace_edit(r.edit, enc)
			end
		end
	end
end
vim.lsp.buf.format({ async = false })

On write, ZLS panics:

[ERROR][2025-03-04 12:16:52] .../vim/lsp/rpc.lua:770	"rpc"	"/nix/store/gnq4msyq5zcf134cq2qwrss8w7c65m0m-zls-0.13.0/bin/zls"	"stderr"	"error: (server): failed to process request-54-textDocument/codeAction: error.ParseError\n"
[ERROR][2025-03-04 12:16:54] .../vim/lsp/rpc.lua:770	"rpc"	"/nix/store/gnq4msyq5zcf134cq2qwrss8w7c65m0m-zls-0.13.0/bin/zls"	"stderr"	"error: (server): failed to process request-63-textDocument/codeAction: error.ParseError\n"
[ERROR][2025-03-04 12:16:56] .../vim/lsp/rpc.lua:770	"rpc"	"/nix/store/gnq4msyq5zcf134cq2qwrss8w7c65m0m-zls-0.13.0/bin/zls"	"stderr"	"error: (server): failed to process request-70-textDocument/codeAction: error.ParseError\n"
[ERROR][2025-03-04 12:17:04] .../vim/lsp/rpc.lua:770	"rpc"	"/nix/store/gnq4msyq5zcf134cq2qwrss8w7c65m0m-zls-0.13.0/bin/zls"	"stderr"	"thread 134148 panic: reached unreachable code\n"
[ERROR][2025-03-04 12:17:04] .../vim/lsp/rpc.lua:770	"rpc"	"/nix/store/gnq4msyq5zcf134cq2qwrss8w7c65m0m-zls-0.13.0/bin/zls"	"stderr"	"Unwind information for `exe:0x12de3cc` was not available, trace may be incomplete\n\nthread 134150 panic: reached unreachable code\n"
[ERROR][2025-03-04 12:17:04] .../vim/lsp/rpc.lua:770	"rpc"	"/nix/store/gnq4msyq5zcf134cq2qwrss8w7c65m0m-zls-0.13.0/bin/zls"	"stderr"	"Unwind information for `exe:0x12de3cc` was not available, trace may be incomplete\n\n"

And the callback fails:

Error detected while processing BufWritePre Autocommands for "<buffer=24>":
Error executing lua callback: ...ovim-unwrapped-0.10.4/share/nvim/runtime/lua/vim/lsp.lua:900: attempt to index local 'client' (a nil value)
stack traceback:
	...ovim-unwrapped-0.10.4/share/nvim/runtime/lua/vim/lsp.lua:900: in function 'cancel'
	...ovim-unwrapped-0.10.4/share/nvim/runtime/lua/vim/lsp.lua:970: in function 'buf_request_sync'
	/home/ben/.config/nvim/init.lua:481: in function </home/ben/.config/nvim/init.lua:472>

I have found that including any additional lines of code to the file (const decl or function) will not trigger this panic, so it seems to be related to the edge case of a single-line file.

Expected Behavior

No change to the file (code action should do nothing). It should have the same effect as running the code action on a two-line file like:

const std = @import("std");
const json = std.json;

Relevant log output

@ben-krieger ben-krieger added the bug Something isn't working label Mar 4, 2025
@Techatrix
Copy link
Member

I have tested this with Zig 0.14.0 and ZLS 0.14.0-dev.412+81407cd and while I did not encounter a panic, ZLS still exited with an error because of a parsing error. After tracing the received messages, the cause is the code action request which looked like this:

{
  "params": {
    "context": { "only": ["source.organizeImports"] },
    "textDocument": { "uri": "file:///home/techatrix/repos/zls/sample.zig" },
    "range": {
      "start": { "character": 0, "line": 0 },
      "end": { "character": 0, "line": 0 }
    }
  },
  "jsonrpc": "2.0",
  "id": 5,
  "method": "textDocument/codeAction"
}

This is invalid according to the LSP Specification because CodeActionContext must have a diagnostics field.

The following change has fixed the issue for me:

- params.context = { only = { "source.organizeImports" } }
+ params.context = { only = { "source.organizeImports" }, diagnostics = {} }

@ben-krieger
Copy link
Contributor Author

That certainly fixed the ParseError for me.

  1. Should ZLS be more tolerant of a missing required field, since other LSPs seem to be this way?
  2. If it's okay, I'll wait to close this issue once nixpkgs is updated and I confirm the panic is gone.

Thank you for helping me debug this! For the future, what's the best way to enable and check debug logs for ZLS in Neovim? It didn't seem to be in settings, so I was just going to patch ZLS in my Nix config...

diff --git a/build.zig b/build.zig
index 07c35fd..d70a4dc 100644
--- a/build.zig
+++ b/build.zig
@@ -37,7 +37,7 @@ pub fn build(b: *Build) !void {

     const single_threaded = b.option(bool, "single-threaded", "Build a single threaded Executable");
     const pie = b.option(bool, "pie", "Build a Position Independent Executable");
-    const enable_tracy = b.option(bool, "enable_tracy", "Whether tracy should be enabled.") orelse false;
+    const enable_tracy = b.option(bool, "enable_tracy", "Whether tracy should be enabled.") orelse true;
     const enable_tracy_allocation = b.option(bool, "enable_tracy_allocation", "Enable using TracyAllocator to monitor allocations.") orelse enable_tracy;
     const enable_tracy_callstack = b.option(bool, "enable_tracy_callstack", "Enable callstack graphs.") orelse enable_tracy;
     const test_filters = b.option([]const []const u8, "test-filter", "Skip tests that do not match filter") orelse &[0][]const u8{};

And then look in :LspLog?

@Techatrix
Copy link
Member

  1. Should ZLS be more tolerant of a missing required field, since other LSPs seem to be this way?

To put it briefly, ZLS should fix its own issues and the editor should fix their issues. This situation here is a big unique since the cause is not in neovim itself but the user config. Writing custom handling for code actions instead of vim.lsp.buf.code_action is a comparatively advanced use case though. Workarounds like this come into play when one side can't easily or quickly fix their issue. But in this case, only a single had to be changed.

2. If it's okay, I'll wait to close this issue once nixpkgs is updated and I confirm the panic is gone.

Do you mean that you want to wait until nixpkgs has updated to Zig and ZLS 0.14.0? If so, feel free to do so and report back later.

what's the best way to enable and check debug logs for ZLS in Neovim?

This should be covered by the logging guide for ZLS. Be aware that the zls.log is not available in ZLS 0.13.0. For neovim with 0.13.0, the best option is probably to run ZLS with the --enable-debug-log flag or vim.lsp.set_log_level("debug") in the neovim config and then open :LspLog. Unfortunately, neovim is one of the worst editors it terms of LSP log quality I've seen (but ZLS is also to blame for that).

Also, enabling support for the Tracy profiler is unrelated to log messages.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working
Projects
None yet
Development

No branches or pull requests

2 participants