Today, many text editors and IDEs support the Language Server Protocol (LSP). As you all know, LSP is a protocol that provides features such as code completion, syntax highlighting, and error detection, supporting a wide range of programming languages.
Vim will most likely still be alive ten years from now, and so will LSP. There is more than enough merit in adding LSP support to Vim's core functionality.
I would like to discuss and decide on Vim's direction now, while we have the opportunity. There are broadly two approaches:
Plan 1. Vim core implements the LSP client itself
Plan 2. Vim core implements the building blocks needed for an LSP client
The first plan needs little explanation. Having Vim core implement the LSP client directly is the most straightforward way to provide LSP support. This would allow users to take advantage of LSP features simply by using Vim.
The second plan is for Vim core to implement the building blocks needed for an LSP client. This means Vim core would provide the foundation for LSP client functionality. This would enable plugins such as vim-lsp and yegappan/lsp to build fast and stable LSP clients by leveraging core functionality.
As far as I know, the following are currently a burden for LSP clients implemented as Vim plugins:
When obtaining text diffs in Vim script, you would typically call getbufline(1, '$'), but when the text is tens of thousands of lines long, this function call becomes costly — for example, applying semantic highlights can take several seconds. LSP clients should send only the changed portions to the server.
As for applying TextEdits, anyone who has implemented an LSP client will understand — applying TextEdits requires accurately and efficiently reflecting changes to a buffer based on specified positions and lengths, making it a complex and costly operation.
Implementing these in Vim script is extremely difficult, and performance issues are likely to arise. If we choose Plan 2, I believe we need to implement the following three building blocks in Vim core:
fname_to_uri(), fname_from_uri()listener_add(), or a getchanges() function to get the current text diffapplytextedits() to apply TextEdits to a buffer—
Reply to this email directly, view it on GitHub, or unsubscribe.
You are receiving this because you are subscribed to this thread.![]()
This is reference implementation of getting text diffs using listener_add()
—
Reply to this email directly, view it on GitHub.
You are receiving this because you are subscribed to this thread.![]()
What about combined approach?
An official lsp plugin bundled with vim. With additional building blocks you mentioned in the last paragraph when and if needed.
There would be quite a few global/local settings users would need to set up and exposing/using them via core c would be quite cumbersome compared to vimscript.
—
Reply to this email directly, view it on GitHub.
You are receiving this because you are subscribed to this thread.![]()
Today, many text editors and IDEs support the Language Server Protocol (LSP). As you all know, LSP is a protocol that provides features such as code completion, syntax highlighting, and error detection, supporting a wide range of programming languages.
Vim will most likely still be alive ten years from now, and so will LSP. There is more than enough merit in adding LSP support to Vim's core functionality.
I would like to discuss and decide on Vim's direction now, while we have the opportunity. There are broadly two approaches:
Plan 1. Vim core implements the LSP client itself
Plan 2. Vim core implements the building blocks needed for an LSP clientThe first plan needs little explanation. Having Vim core implement the LSP client directly is the most straightforward way to provide LSP support. This would allow users to take advantage of LSP features simply by using Vim.
The second plan is for Vim core to implement the building blocks needed for an LSP client. This means Vim core would provide the foundation for LSP client functionality. This would enable plugins such as vim-lsp and yegappan/lsp to build fast and stable LSP clients by leveraging core functionality.
As far as I know, the following are currently a burden for LSP clients implemented as Vim plugins:
- Obtaining text diffs
- Applying TextEdits
When obtaining text diffs in Vim script, you would typically call
getbufline(1, '$'), but when the text is tens of thousands of lines long, this function call becomes costly — for example, applying semantic highlights can take several seconds. LSP clients should send only the changed portions to the server.As for applying TextEdits, anyone who has implemented an LSP client will understand — applying TextEdits requires accurately and efficiently reflecting changes to a buffer based on specified positions and lengths, making it a complex and costly operation.
Implementing these in Vim script is extremely difficult, and performance issues are likely to arise. If we choose Plan 2, I believe we need to implement the following three building blocks in Vim core:
fname_to_uri(),fname_from_uri()- A way to obtain text diffs using
listener_add(), or agetchanges()function to get the current text diffapplytextedits()to apply TextEdits to a buffer
—
Reply to this email directly, view it on GitHub.
You are receiving this because you are subscribed to this thread.![]()
—
Reply to this email directly, view it on GitHub.
You are receiving this because you are subscribed to this thread.![]()
That would be that one here: #19003 I guess
—
Reply to this email directly, view it on GitHub.
You are receiving this because you commented.![]()
In your opinion, should Vim implement an LSP client rather than being a component for LSP clients?
—
Reply to this email directly, view it on GitHub.
You are receiving this because you commented.![]()
A C based LSP client built-in to vim?
It would be good to have. Is it feasible given the fact several independent plugins are available and the burden it would add for the maintainers? I don't know.
—
Reply to this email directly, view it on GitHub.
You are receiving this because you commented.![]()
Thank you. We're currently in the phase of deciding which of those to pursue, so your input is very helpful.
—
Reply to this email directly, view it on GitHub.
You are receiving this because you commented.![]()
I think this should be implemented in Vim9script so that more people can contribute to this. I looked into implementing the LSP support in core Vim a few years ago along the lines of the existing Netbeans support. But decided against doing that as only a few people will contribute to the C based implementation.
—
Reply to this email directly, view it on GitHub.
You are receiving this because you commented.![]()
I'm just curious, what makes implementing string identifiers non-trivial? Would it be possible to use a hash table to match LSP requests with responses?
—
Reply to this email directly, view it on GitHub.
You are receiving this because you commented.![]()
@64-bitman Yes. The LSP feature depends on the Vim channel feature. The channel code can be extended to support string identifiers. Someone needs to work on this.
—
Reply to this email directly, view it on GitHub.
You are receiving this because you commented.![]()
@mattn have you identified the slow parts of getbufline(1, '$').
I can recommend valgrind + kcachegrind.
diff --git a/src/Makefile b/src/Makefile
index 99c77cb34..df6f1e6f5 100644
--- a/src/Makefile
+++ b/src/Makefile
@@ -587,7 +587,7 @@ CClink = $(CC)
# When using -g with some older versions of Linux you might get a
# statically linked executable.
# When not defined, configure will try to use -O2 for gcc and -O for others.
-#CFLAGS = -g
+CFLAGS = -g
#CFLAGS = -O
# Optimization limits - depends on the compiler. Automatic check in configure
valgrind --tool=callgrind --dump-instr=yes --callgrind-out-file=callgrind.out ./vim
just open callgrind.out in kcachegrind and you can hit F5 to reload between builds, you might need to add the /src dir in config dialog to get source view.
—
Reply to this email directly, view it on GitHub.
You are receiving this because you commented.![]()
The issue is not that getbufline() itself is slow internally — it's that to compute a diff for incremental text synchronization in LSP, the Vim script LSP client must call getbufline(1, '$') to get the entire buffer content, even when only a single line has changed. For a 30,000-line buffer, that means copying 30,000 strings into a list just to find the one line that changed. The bottleneck is the unnecessary work of retrieving and diffing the full buffer, not the implementation of getbufline() per se. That's exactly why I proposed listener_add() or getchanges() — so the client can be notified of what changed without having to scan the entire buffer.
—
Reply to this email directly, view it on GitHub.
You are receiving this because you commented.![]()
Thanks mattn for starting this discussion!
I am very interested in this topic as I haven't found a satisfactory LSP Client for vim. The ones I have found are either unacceptably slow or incomplete. I have my own vim9 LSP Client but Server Synchronization isn't complete.
Being 2026, it sure seems as though Vim needs a good LSP Client!
I've looked at the solution provided by prabirshrestha using listener_add(). While headed in the right direction (IMO), I believe a solution in vim9 is needed.
I favor your plan #2
Plan 2. Vim core implements the building blocks needed for an LSP client
I have one question regarding your two tasks:
Obtaining text diffs
Applying TextEdits
The goal is maintaining sync with Server. Are you thinking "Appying TextEdits" is relaying changes to the Server via textDocument/didChange? With perhaps some consolidation of changes as reported to the listener callback fcn?
—
Reply to this email directly, view it on GitHub, or unsubscribe.
You are receiving this because you commented.![]()
I spent a few more hours digging into the LSP Server Incremental Synchronization Issue. This is for others who may come along investigating this issue. Its clear that even with the latest vim (9.2), this is a difficult (& ugly) nut to crack.
The "listener fcn" set via listener_add() simply doesn't provide the necessary information to easily computer the range & text values required the the LSP textDocument/didChange notify msg. The range value needs to reflect the start/end locations of any deleted or replaced text which can't be gleaned from the information provided to the listener fcn. I looked into the undotree() and that route didn't look promising either.
The best route I can determine, which appears to be the route taken by prabirshrestha, is to keep two versions of any file being tracked by a LSP Server and then do a "diff" between them. The number of diffed lined can be minimized with information provided to the listener fcn but diffing is req'd none the less.
So, for those working on vim's internal code, it would be great if listener_add(), or an improved version (get_changes() ?? as mattn suggests ??), that provides a [was/is] list would be great. Absent of that, the next best solution is a vim9 solution that performs the required diffs to format the req'd textDocument/didChange msg.
As far as I know, a vim9 solution doesn't exist. If anyone has developed this, please drop a comment here so those of us wanting a decent LSP Client can benefit. BTW: code folding is completely unworkable without vim9.
—
Reply to this email directly, view it on GitHub, or unsubscribe.
You are receiving this because you commented.![]()