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

Add LspComplete autocmd #25788

Closed
ColinKennedy opened this issue Oct 26, 2023 · 5 comments
Closed

Add LspComplete autocmd #25788

ColinKennedy opened this issue Oct 26, 2023 · 5 comments
Labels
enhancement feature request lsp status:blocked-external Needs a third-party / external change or fix

Comments

@ColinKennedy
Copy link
Sponsor Contributor

ColinKennedy commented Oct 26, 2023

Problem

Some plug-ins that I use only function if LSP is available and has already run on a particular buffer. If I wanted to delay-load these plug-ins with a plug-in manager like lazy.nvim, there's currently no straightforward way to do this. Existing related autocmds such as :h LspRequest and :h LspProgressUpdate are spammy and also get called too early.

It'd be nice if there was a "LSP is done now" side-effect, which motivated the idea of a new LspComplete autocmd.

I've got a User autocmd that basically implements what I had in mind if you want to check it out (A big thanks to fidget.nvim for inspiring this code)

Click to expand
local vim_closing = false
local tasks = {}


local function _kill_task(task)
    tasks[task] = nil
end


local function _has_tasks()
  return next(tasks)
end


local function _handle_progress(err, msg, info)
    -- See: https://microsoft.github.io/language-server-protocol/specifications/specification-current/#progress
    if vim_closing then
        return
    end

    local task = msg.token
    local value = msg.value

    if not task then
      -- Notification missing required token??
      return
    end

    tasks[task] = true

    if value.kind == "end" then
        _kill_task(task)
    end

    if not _has_tasks() then
        vim.api.nvim_exec_autocmds("User", {pattern = "LspComplete"})

        return
    end
end


local old_handler = vim.lsp.handlers["$/progress"]

vim.lsp.handlers["$/progress"] = function(...)
    old_handler(...)
    _handle_progress(...)
end


local group = vim.api.nvim_create_augroup("LspCustomGroup", { clear = true })

vim.api.nvim_create_autocmd(
    "User",
    { callback = function() end, group = group, pattern = "LspComplete" }
)

And is callable with

vim.cmd("autocmd User LspComplete :echoerr 'blah'")

This request might tie into or be related to future implementations of #25714

Expected behavior

An autocmd that is called after LSP has been fully attached and initialized on some buffer. I think it'd be fine it if is called more than once if the LSP needs to re-index or do some other kind of work. But ideally this should be at the "end" of that work

@ColinKennedy ColinKennedy added the enhancement feature request label Oct 26, 2023
@mfussenegger
Copy link
Member

mfussenegger commented Oct 26, 2023

You could use the LspAttach, or LspRequest autocmd and react only to initialize events. After these the servers are ready to handle requests.

The protocol doesn't have any other special mechanism to identify when a server has indexed a project, or if it finished compilation or analysis. Further, any text changes you make can always trigger a re-compilation or analysis.

@zeertzjq zeertzjq added the lsp label Oct 26, 2023
@ColinKennedy
Copy link
Sponsor Contributor Author

Could you expand on the LspAttach / LspRequest suggestion? LspAttach appears to run too early for the plugins that this use-case is for and LspRequest is very spammy. I don't know what you mean by reacting only to initialize events. I suppose that is the detail from your suggestion that I'm missing.

As for triggering re-compilation / analysis, that's not much of a concern in this use-case as the intent is to make plug-ins available after the first time (at least) that the LSP is indexed and ready. If the event triggers once again later, though not ideal, it's still certainly an improvement. https://github.com/simrat39/symbols-outline.nvim is a decent example. It cannot display until the LSP is done checking the files for the first time.

@justinmk
Copy link
Member

Expected behavior: An autocmd that is called after LSP has been fully attached and initialized on some buffer.

We would not add a new event for this, it could perhaps be a v:event field. But as alluded to above, see :help vim.lsp.buf.server_ready() and #23520 (review) :

there really is no good alternative in the specification to detect if a server is ready I'm inclined to merge this. If my interpretation of the spec is correct, after the initialize handshake a server should be ready to serve requests.

... edits can always re-trigger analysis/builds that's always a catch-up game, and it's also unclear what an editor should do if the server is still processing changes. It is also not a simple ready/not-ready, because depending on the implementations they can serve some requests with sensible results, some with outdated, some not at all.

@justinmk justinmk closed this as not planned Won't fix, can't repro, duplicate, stale Oct 28, 2023
@justinmk justinmk added the status:blocked-external Needs a third-party / external change or fix label Oct 28, 2023
@mfussenegger
Copy link
Member

mfussenegger commented Oct 29, 2023

Could you expand on the LspAttach / LspRequest suggestion? LspAttach appears to run too early for the plugins that this use-case is for and LspRequest is very spammy

  • LspAttach runs whenever a client attaches to a buffer
  • LspRequest runs on any request, but you can filter on the payload. It contains a request property in the data. See :help LspRequest for more details. (On nightly, in 0.9 the command was a User variant and the docs are less complete)
  • LspProgress runs whenever the client receives a progress notification from the server.

The problem is that there is no defined "I finished analysing the project" event. Even if we wanted, we couldn't add a LspComplete event.

I think your best option is to continue using LspProgress and de-register the handler once the server is initialized.
Or you could raise an issue in the language-server-protocol repository and ask for an addition. I think formalizing a $/progress payload that indicates that a analysis cycle completed could have a chance getting accepted.

@ColinKennedy
Copy link
Sponsor Contributor Author

ColinKennedy commented Oct 29, 2023

Thank you for those details, I can see the bigger picture now! I'll try out LspProgress as you've recommended for my needs

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement feature request lsp status:blocked-external Needs a third-party / external change or fix
Projects
None yet
Development

No branches or pull requests

4 participants