You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
I'd like to do the same for Java, but it seems that starting jdtls is hardcoded in the language_server_command function, and that currently there's no way to change it.
I'm new to Zed extensions and to Rust too, so it's possible I missed something.
The same works for jdtls, you can provide a binary which is going to bypass the extension's LSP completely. The extension would be limited at providing syntax highlighting pretty much.
I use nc because I want to connect to an already running LSP server process.
Just for the record, I had a slightly wrong mental model. I expected the following to work, but it didn't, neither for Kotlin nor Java. It's a shame, I don't see why the names of LSPs are hardcoded in the extensions.
Well, there's a slightly different problem in that the LSP I want to use supports two languages at the same time, so I'd like Zed to connect to the LSP just once. But it seems not possible.
@yeskunall@bartekpacia could you point me somewhere to find more information about your particular approach, and the reasoning behind it and the other tools involved etc? If we understand it better we might be able to properly support it, after all this is the Java extension not just the JDTLS extension, and I can imagine Kotlin interop to be in scope eventually (I gather that this is what you are trying to achieve)
At JetBrains, we're building a Kotlin LSP. One of Kotlin's main features is full interoperability with Java, so our LSP has a certain (very minimal and barebones) amount of support for Java.
This is where my question/request comes from - a single LSP server process handling both languages. I was researching the support for this scenario among some open-source editors, and so far, I haven't been able to support this use case in Zed.
In principle I don't see why that would not work. If there is a program that reliably takes care of distributing the LSP-messages between language servers there could be a small protocol for discovering existing processes and using that as a facade instead of launching JDTLS. That way Kotlin can get/send java-related LSP-messages, and presumably that program would use JDTLS under the hood to supply the more sophisticated Java-related functionality.
So what we would need to do:
define the protocol for discovering running instances of the facade or launching it
build the facade in a cross-platform way (I doubt simple nc is going to be convenient in the long run)
add a config option in the extension to use that facade instead of jdtls directly.
For testing and poc, you can also just specify an arbitrary executable in the config under lsp.jdtls.settings.jdtls_launcher that could point to a script that does the discovery and proxies the messages.
Well, we already have the use case I described above working with VSCode, with very little effort. The VSCode extension has the following in its package.json:
So from what I understand is that you want to have highlighting etc. done by two different extensions but both of those extensions connecting to the same running instance of a language server for completions and diagnostics.
The way you would do that in current Zed (for extension languages such as Java and Kotlin) is tell one of those extensions to launch a script that will discover and connect to the already running instance and forward the messages, in the language_server_command method of the Extension trait implementation. Zed will think it's running two distinct language servers, but the messages will all arrive at and come from the same one, and as long as they are LSP-compliant and coordinated it should work.
In the java extension you can do that by specifying the fully qualified path to your script in lsp.jdtls.settings.jdtls_launcher in settings. This is not actually tied to jdtls in any way, it just expects an LSP-compliant server at the other end, and a proxy will do just fine. In fact, this extension already launches a (node) proxy in order to hack around some of the current limitations of Zeds extension infrastructure.
So if you create a shell script that does nc localhost 9999 and set that as the jdtls_launcher, this extension should treat your script as the language server for java.
Thank you for such a thorough and detailed response!
So from what I understand is that you want to have highlighting etc. done by two different extensions
Ah, for VSCode, we have a single extension for now, and it handles both (the minimal amount of) Java and Kotlin. We'd like the same for Zed.
I guess this is the main problem:
in VSCode, we have a single extension, which registers itself for both Java and Kotlin (initialize request is send once)
in Zed, there is a separate Java extension and a separate Kotlin extension (initialize request is send twice)
I'm thinking one of the solutions here is to have Kotlin extension (probably conditionally? is it possible?) handle both languages?
So from what I understand is that you want to have highlighting etc. done by two different extensions but both of those extensions connecting to the same running instance of a language server for completions and diagnostics.
This is (in principle) correct, but it's probably easier to just have a single Zed extension handle this.
Activity
tartarughina commentedon Dec 5, 2025
The same works for jdtls, you can provide a binary which is going to bypass the extension's LSP completely. The extension would be limited at providing syntax highlighting pretty much.
bartekpacia commentedon Dec 5, 2025
Ah, right, thanks! This works:
{ "languages": { "Kotlin": { "language_servers": ["kotlin-lsp"] }, "Java": { "language_servers": ["jdtls"] } }, "lsp": { "kotlin-lsp": { "binary": { "path": "nc", "arguments": ["localhost", "9998"] } }, "jdtls": { "binary": { "path": "nc", "arguments": ["localhost", "9999"] } } } }I use
ncbecause I want to connect to an already running LSP server process.Just for the record, I had a slightly wrong mental model. I expected the following to work, but it didn't, neither for Kotlin nor Java. It's a shame, I don't see why the names of LSPs are hardcoded in the extensions.
{ "languages": { "Kotlin": { "language_servers": ["lsp-for-kotlin"] }, "Java": { "language_servers": ["lsp-for-java"] } }, "lsp": { "lsp-for-kotlin": { "binary": { "path": "nc", "arguments": ["localhost", "9998"] } }, "lsp-for-java": { "binary": { "path": "nc", "arguments": ["localhost", "9999"] } } } }yeskunall commentedon Dec 5, 2025
@bartekpacia what happens if you do this?:
{ "languages": { "Java": { "language_servers": ["nc", "!jdtls", "..."] } }, "lsp": { "nc": { "binary": { "path": "nc", "arguments": ["localhost", "9999"] } } } }bartekpacia commentedon Dec 5, 2025
@yeskunall Nothing – Zed doesn't even start the LSP:
yeskunall commentedon Dec 5, 2025
Hmm, this might be a bug. I’ll look into it. For now, does your workaround fully let you work with
ncin Zed?bartekpacia commentedon Dec 6, 2025
@yeskunall Thanks! Yes, I can work just fine :)
Well, there's a slightly different problem in that the LSP I want to use supports two languages at the same time, so I'd like Zed to connect to the LSP just once. But it seems not possible.
playdohface commentedon Dec 6, 2025
@yeskunall @bartekpacia could you point me somewhere to find more information about your particular approach, and the reasoning behind it and the other tools involved etc? If we understand it better we might be able to properly support it, after all this is the Java extension not just the JDTLS extension, and I can imagine Kotlin interop to be in scope eventually (I gather that this is what you are trying to achieve)
bartekpacia commentedon Dec 6, 2025
Sure!
At JetBrains, we're building a Kotlin LSP. One of Kotlin's main features is full interoperability with Java, so our LSP has a certain (very minimal and barebones) amount of support for Java.
This is where my question/request comes from - a single LSP server process handling both languages. I was researching the support for this scenario among some open-source editors, and so far, I haven't been able to support this use case in Zed.
playdohface commentedon Dec 7, 2025
In principle I don't see why that would not work. If there is a program that reliably takes care of distributing the LSP-messages between language servers there could be a small protocol for discovering existing processes and using that as a facade instead of launching JDTLS. That way Kotlin can get/send java-related LSP-messages, and presumably that program would use JDTLS under the hood to supply the more sophisticated Java-related functionality.
So what we would need to do:
ncis going to be convenient in the long run)For testing and poc, you can also just specify an arbitrary executable in the config under
lsp.jdtls.settings.jdtls_launcherthat could point to a script that does the discovery and proxies the messages.bartekpacia commentedon Dec 7, 2025
Well, we already have the use case I described above working with VSCode, with very little effort. The VSCode extension has the following in its
package.json:And the user can configure the port for the extension in
settings.jsonlike this:so our VSCode extension connects to an already running LSP on that port.
Maybe I'm missing something (again, sorry, I'm quite new to Zed), but I don't know how to achieve the same behavior with Zed.
playdohface commentedon Dec 8, 2025
So from what I understand is that you want to have highlighting etc. done by two different extensions but both of those extensions connecting to the same running instance of a language server for completions and diagnostics.
The way you would do that in current Zed (for extension languages such as Java and Kotlin) is tell one of those extensions to launch a script that will discover and connect to the already running instance and forward the messages, in the
language_server_commandmethod of theExtensiontrait implementation. Zed will think it's running two distinct language servers, but the messages will all arrive at and come from the same one, and as long as they are LSP-compliant and coordinated it should work.In the java extension you can do that by specifying the fully qualified path to your script in
lsp.jdtls.settings.jdtls_launcherin settings. This is not actually tied to jdtls in any way, it just expects an LSP-compliant server at the other end, and a proxy will do just fine. In fact, this extension already launches a (node) proxy in order to hack around some of the current limitations of Zeds extension infrastructure.So if you create a shell script that does
nc localhost 9999and set that as thejdtls_launcher, this extension should treat your script as the language server for java.bartekpacia commentedon Dec 9, 2025
Thank you for such a thorough and detailed response!
Ah, for VSCode, we have a single extension for now, and it handles both (the minimal amount of) Java and Kotlin. We'd like the same for Zed.
I guess this is the main problem:
initializerequest is send once)initializerequest is send twice)I'm thinking one of the solutions here is to have Kotlin extension (probably conditionally? is it possible?) handle both languages?
This is (in principle) correct, but it's probably easier to just have a single Zed extension handle this.