[vim/vim] LSP Support in Vim Core (Discussion #19621)

59 views
Skip to first unread message

mattn

unread,
Mar 9, 2026, 8:15:57 PMMar 9
to vim/vim, Subscribed

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:

  • 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 a getchanges() function to get the current text diff
  • applytextedits() 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.Message ID: <vim/vim/repo-discussions/19621@github.com>

mattn

unread,
Mar 9, 2026, 8:18:59 PMMar 9
to vim/vim, Subscribed

This is reference implementation of getting text diffs using listener_add()

prabirshrestha/vim-lsp#1653


Reply to this email directly, view it on GitHub.
You are receiving this because you are subscribed to this thread.Message ID: <vim/vim/repo-discussions/19621/comments/16059588@github.com>

Maxim Kim

unread,
Mar 9, 2026, 9:56:30 PMMar 9
to vim/vim, Subscribed

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.Message ID: <vim/vim/repo-discussions/19621/comments/16060588@github.com>

Yegappan Lakshmanan

unread,
Mar 9, 2026, 11:59:25 PMMar 9
to vim...@googlegroups.com, reply+ACY5DGDF327NKC3L3V...@reply.github.com, vim/vim, Subscribed
Hi,

On Mon, Mar 9, 2026 at 5:15 PM mattn <vim-dev...@256bit.org> wrote:

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.


I prefer the second option as that is more flexible and allows more custom plugins to be developed.
 

As far as I know, the following are currently a burden for LSP clients implemented as Vim plugins:

  • Obtaining text diffs
  • Applying TextEdits

The enhancements to the listener functionality in Vim 9.2 are supposed to simplify the method to get the
changes. I haven't tried it out yet as I want the LSP plugin to work with Vim 9.0.

I also added the diff() function to simplify getting the diff between a cached version of a buffer and the
latest version.
 

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 a getchanges() function to get the current text diff
  • applytextedits() to apply TextEdits to a buffer


We also need functions to better handle snippets with multiple variables.

Regards,
Yegappan 

vim-dev ML

unread,
Mar 9, 2026, 11:59:53 PMMar 9
to vim/vim, vim-dev ML, Your activity

Hi,

On Mon, Mar 9, 2026 at 5:15 PM mattn ***@***.***> wrote:

> 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.
>

I prefer the second option as that is more flexible and allows more custom
plugins to be developed.


> As far as I know, the following are currently a burden for LSP clients
> implemented as Vim plugins:
>
> - Obtaining text diffs
> - Applying TextEdits
>
>
The enhancements to the listener functionality in Vim 9.2 are supposed to
simplify the method to get the
changes. I haven't tried it out yet as I want the LSP plugin to work with
Vim 9.0.

I also added the diff() function to simplify getting the diff between a
cached version of a buffer and the
latest version.


>
>
> 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 a getchanges()
> function to get the current text diff
> - applytextedits() to apply TextEdits to a buffer
>
>
> We also need functions to better handle snippets with multiple variables.

Regards,
Yegappan


Reply to this email directly, view it on GitHub.
You are receiving this because you are subscribed to this thread.Message ID: <vim/vim/repo-discussions/19621/comments/16061639@github.com>

Yegappan Lakshmanan

unread,
Mar 10, 2026, 11:15:35 AMMar 10
to vim...@googlegroups.com, reply+ACY5DGDF327NKC3L3V...@reply.github.com, vim/vim, Subscribed
Another feature we need is support for string identifiers within the
Vim channel interface.
While the Language Server Protocol specification allows for both
numeric and string identifiers
in JSON-RPC messages, the Vim channel interface currently only
supports numeric ones.

Regards,
Yegappan

vim-dev ML

unread,
Mar 10, 2026, 11:16:01 AMMar 10
to vim/vim, vim-dev ML, Your activity

On Mon, Mar 9, 2026 at 8:59 PM Yegappan Lakshmanan ***@***.***> wrote:
>
> Hi,
>
> On Mon, Mar 9, 2026 at 5:15 PM mattn ***@***.***> wrote:
>>
>> 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.
>
>
> I prefer the second option as that is more flexible and allows more custom plugins to be developed.
>
>>
>> As far as I know, the following are currently a burden for LSP clients implemented as Vim plugins:
>>
>> Obtaining text diffs
>> Applying TextEdits
>
>
> The enhancements to the listener functionality in Vim 9.2 are supposed to simplify the method to get the
> changes. I haven't tried it out yet as I want the LSP plugin to work with Vim 9.0.
>
> I also added the diff() function to simplify getting the diff between a cached version of a buffer and the
> latest version.
>
>>
>> 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 a getchanges() function to get the current text diff
>> applytextedits() to apply TextEdits to a buffer
>>
>>
> We also need functions to better handle snippets with multiple variables.
>

Another feature we need is support for string identifiers within the
Vim channel interface.
While the Language Server Protocol specification allows for both
numeric and string identifiers
in JSON-RPC messages, the Vim channel interface currently only
supports numeric ones.

Regards,
Yegappan


Reply to this email directly, view it on GitHub.
You are receiving this because you are subscribed to this thread.Message ID: <vim/vim/repo-discussions/19621/comments/16070456@github.com>

Christian Brabandt

unread,
Mar 10, 2026, 11:52:25 AMMar 10
to vim/vim, vim-dev ML, Comment

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.Message ID: <vim/vim/repo-discussions/19621/comments/16070882@github.com>

mattn

unread,
Mar 11, 2026, 9:08:03 PMMar 11
to vim/vim, vim-dev ML, Comment

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.Message ID: <vim/vim/repo-discussions/19621/comments/16090686@github.com>

Maxim Kim

unread,
Mar 11, 2026, 10:57:49 PMMar 11
to vim/vim, vim-dev ML, Comment

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.Message ID: <vim/vim/repo-discussions/19621/comments/16091390@github.com>

mattn

unread,
Mar 12, 2026, 12:03:11 AMMar 12
to vim/vim, vim-dev ML, Comment

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.Message ID: <vim/vim/repo-discussions/19621/comments/16091869@github.com>

Yegappan Lakshmanan

unread,
Mar 12, 2026, 12:57:48 AMMar 12
to vim/vim, vim-dev ML, Comment

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.Message ID: <vim/vim/repo-discussions/19621/comments/16092219@github.com>

Foxe Chen

unread,
Mar 12, 2026, 7:59:20 AMMar 12
to vim/vim, vim-dev ML, Comment

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.Message ID: <vim/vim/repo-discussions/19621/comments/16096962@github.com>

Yegappan Lakshmanan

unread,
Mar 12, 2026, 11:15:47 AMMar 12
to vim/vim, vim-dev ML, Comment

@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.Message ID: <vim/vim/repo-discussions/19621/comments/16099463@github.com>

dezza

unread,
Mar 14, 2026, 8:20:05 PMMar 14
to vim/vim, vim-dev ML, Comment

@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.Message ID: <vim/vim/repo-discussions/19621/comments/16135018@github.com>

mattn

unread,
Mar 18, 2026, 3:31:33 AMMar 18
to vim/vim, vim-dev ML, Comment

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.Message ID: <vim/vim/repo-discussions/19621/comments/16188795@github.com>

cagledw

unread,
Mar 30, 2026, 10:10:09 AM (4 days ago) Mar 30
to vim/vim, vim-dev ML, Comment

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.Message ID: <vim/vim/repo-discussions/19621/comments/16381646@github.com>

cagledw

unread,
1:11 PM (8 hours ago) 1:11 PM
to vim/vim, vim-dev ML, Comment

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.Message ID: <vim/vim/repo-discussions/19621/comments/16440562@github.com>

Reply all
Reply to author
Forward
0 new messages