Comments (13)
Thanks for the repro.
I'm not entirely sure if #29170 is a sound approach, but it would fix the issue as far as I can tell.
from neovim.
But I suspect we're going to have similar issues with the semantic tokens & inlay hints.
I was wondering about that too, but can't think of a scenario where it could be a problem. If the buffer is unchanged (modified=false), there shouldn't be any updates for semantic tokens, inlay hints, code lens and so on anyway.
from neovim.
I've noticed recently as well (even after the above follow up fix for the modify case) that semantic tokens aren't updating as reliably as they were. When I first implemented that feature, I remember looking into changedtick vs the way we were tracking versions and came to the conclusion at the time that we couldn't use changedtick and what we were doing was necessary, but I don't remember exactly how I came to that conclusion.
I'll try to dig into that again at some point soon to see if I can remember why changedtick wasn't sufficient.
from neovim.
from neovim.
Repro:
init.lua
vim.api.nvim_create_autocmd("FileType", {
pattern = "go",
callback = function()
vim.lsp.start({ cmd = { "gopls" } })
end
})
main.go
// delete me
package main
func main() {
fmt.Println()
}
Steps:
nvim --clean -u init.lua main.go
- Delete the first line
dd
- Save the buffer
:w
- Run code action
:lua vim.lsp.buf.code_action({ apply = true })
from neovim.
The change seems reasonable to me (I tested locally and can't break it). But I suspect we're going to have similar issues with the semantic tokens & inlay hints. If that does end up being the case and more edge-case handling is required, I think it might make sense to revert #28943
from neovim.
I'm having a difficult time figuring out how a plugin should call workspace/executeCommand
with the right version. I'm using the :EslintFixAll
command as the example here.
The original (current) implementation has the problem where the version is being rejected on the server side because it's too new.
local bufnr = util.validate_bufnr(opts.bufnr or 0)
request(0, 'workspace/executeCommand', {
command = 'eslint.applyAllFixes',
arguments = {
{
uri = vim.uri_from_bufnr(bufnr),
version = lsp.util.buf_versions[bufnr],
},
},
})
I tried rewriting it to use vim.lsp.buf.code_action
. This doesn't work because code_action
automatically populates the request context
with diagnostics from the current line. (This results in the eslint.applyAllFixes
only fixing the issues on the current line).
lsp.buf.code_action({
apply = true,
filter = function(a)
return vim.tbl_get(a, 'command', 'command') == 'eslint.applyAllFixes'
end,
})
I would need to use the unexported diagnostic_vim_to_lsp
from vim.lsp.diagnostic
to send all the diagnostics in the current buffer to achieve the original behaviour.
lsp.buf.code_action({
apply = true,
context = {
diagnostics = diagnostic_vim_to_lsp(vim.diagnostic.get(0)),
},
filter = function(a)
return vim.tbl_get(a, 'command', 'command') == 'eslint.applyAllFixes'
end,
})
This feels like a lot of hoops to jump through to do something relatively simple.
from neovim.
I'm not sure how to best deal with that but I'm reluctant to revert the change. lsp.util
is not the right place for the version.
One option that would work is to apply the same modified check as in the linked fix (version = vim.b[bufnr].changedtick + (not vim.bo[bufnr].modified and 1 or 0)
)
Another option could be to simplify the code_action API by:
- Take vim.diagnostics instead of lsp diagnostics. Given that a user has no means to provide the lsp diagnostics without accessing internals the parameter doesn't make sense anymore
- Support a
string
as filter in addition to the function, that matches on the command.
In general it's a bit unusual that the server even requires a version parameter with the request. Can't it be set to nil
with in other places means the client/server should ignore the version?
from neovim.
I'm not sure how to best deal with that but I'm reluctant to revert the change. lsp.util is not the right place for the version.
It could live in changetracking
and only expose a changetracking.buf_version(bufnr)
function.
- The internal state gets updated by
changetracking.send_changes
(usingvim.b[bufnr].changedtick
). - Returns
0
if there's no existing value. - No need to ever reset back to
0
, just keep incrementing it.
edit it can't live in changetracking due to circular dependencies.
One option that would work is to apply the same modified check as in the linked fix (version = vim.b[bufnr].changedtick + (not vim.bo[bufnr].modified and 1 or 0))
This breaks if you open a file with existing issues.
In general it's a bit unusual that the server even requires a version parameter with the request.
Yes, this is true. I've tried to find other examples to strengthen my case and have came up empty handed.
Can't it be set to nil with in other places means the client/server should ignore the version?
This was the first thing I tried. No luck.
from neovim.
I've been thinking about this a little more and I'm starting to question the use of changedtick
all-together. The behaviour with modified
creates an impedance mismatch between changedtick
and lsp document versions. I propose a lsp/_versions.lua
file with the following:
local M = {}
---@type table<integer, integer>
local buf_versions = {}
---@param bufnr integer
---@return integer
function M.get(bufnr)
-- we default to 1 instead of 0 because some LSPs return 0
-- when they don't support versioning. Starting at 1 disambiguates
-- that case.
return buf_versions[bufnr] or 1
end
---@param bufnr integer
function M.changed(bufnr)
buf_versions[bufnr] = M.get(bufnr) + 1
end
return M
from neovim.
I've also noticed some strangeness with semantic tokens, but I haven't been able to come up with a repro.
from neovim.
Ok I haven't 100% confirmed this, but I think I know why I'm occasionally seeing some weird stuff with semantic tokens.
So, the crux of the issue is that b:changedtick can change without an on_lines()
callback being fired. changedtick is incremented for all sorts of things that don't fire that callback (but on_changedtick()
is fired for all of them). But because we only send change notifications (and re-send new semantic token requests) from on_lines, we can easily get out of sync on the version we think a buffer is on vs whatever was last sent to the server.
Specifically for semantic tokens this means that when a request comes back and the changedtick number changed without firing on_lines, we drop the response without ever sending a new request.
Because we actually only care about changes to a buffer that occur with an on_lines callback, that is why we needed a separate version--one that only increments when we actually get that callback (so we send change notifications, request new tokens, etc).
from neovim.
Reverted it for now #29217
from neovim.
Related Issues (20)
- Document the 0.10 behavior regarding termguicolors HOT 4
- Unexpected active the treesitter parser HOT 19
- All syntax coloring is gone. HOT 1
- extmarks wont get restored with {right_gravity=true,invalidate=true,undo_restore=true} opts HOT 1
- Vim.lsp.rpc.connect() with hostname
- :Tutor shows check mark on to-delete lines
- Support for attaching popup window to an LSP for limited calls
- neovim does not start on windows server 2022 HOT 1
- Editing a file over scp fails on Windows
- Diff: `foldclose` and `wrap` don't sync
- Add alternatives to deprecated functions to doc comments HOT 4
- Visual Highlights Get Stuck (snippet.lua:101: attempt to index a nil value)
- TextChanged event gets triggered with not changed b:changedtick HOT 1
- `BufWinEnter` not triggered after processing modelines HOT 1
- swap file fails to open with a UNC path on Windows
- Compilation fails on arm64 windows on gettext. HOT 5
- [spam]
- .css file extensions add extra asterisks (*) when not supposed to HOT 1
- Error when calling vim.treesitter.start() and vim.treesitter.stop() in init.lua
- Missing heading for "REFERENCE MANUAL" TOC HOT 2
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
D3
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
-
Recommend Topics
-
javascript
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
-
web
Some thing interesting about web. New door for the world.
-
server
A server is a program made to process requests and deliver data to clients.
-
Machine learning
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google ❤️ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from neovim.