Skip to content

Implement java support#1

Open
aunncodes wants to merge 13 commits intocpinitiative:mainfrom
aunncodes:jdtls
Open

Implement java support#1
aunncodes wants to merge 13 commits intocpinitiative:mainfrom
aunncodes:jdtls

Conversation

@aunncodes
Copy link

@aunncodes aunncodes commented Feb 11, 2026

Note: This code isn't as clean as I hoped to make it, it simply has blocks of if checks inside the lsp server. If anyone has a cleaner way to implement it, send it my way and I will fix up the code. Also, in order to test this pr, you will also have to change a line in ide utils.ts, swapping
uri: /workspace/main.${language},
to be
uri: language === 'java' ? 'file:///workspace/main.java' : /workspace/main.${language},
and of course using a ternary operator like
language === 'cpp'
? 'clangd'
: language === 'java'
? 'jdtls'
: 'pyright'
}`;
so that jdtls also functions.

Additional Concern:
Not sure if this will work for many users, as it was hard to test for that locally. It does use temporary directories, pretty much identical to the cpp compiler options thing, so I think it would, but just thought I would mention that.


Open with Devin

@thecodingwizard
Copy link
Member

@cursor review

@thecodingwizard
Copy link
Member

bugbot run

@aunncodes
Copy link
Author

bugbot run

Sorry, should've moved this to draft. Already got some feedback through @emhgit, and will fix up this pr to make the code nicer soon.

@aunncodes aunncodes marked this pull request as draft February 14, 2026 07:23
@aunncodes aunncodes marked this pull request as ready for review February 18, 2026 01:32
aunncodes and others added 6 commits February 17, 2026 20:53
@thecodingwizard
Copy link
Member

@cursor review

Copy link

@devin-ai-integration devin-ai-integration bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Devin Review found 1 potential issue.

View 6 additional findings in Devin Review.

Open in Devin Review

Comment on lines +59 to +62
elif method == "textDocument/didChange":
text = obj["params"]["contentChanges"][-1]["text"]
with open(self._jdtls_main_path, "w", encoding="utf-8") as f:
f.write(text)

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔴 didChange handler writes incremental text delta as full file content

The didChange handler in JdtlsAdapter.ws_to_lsp writes contentChanges[-1]["text"] to the file as if it were the full document content. This is only correct when the language server uses TextDocumentSyncKind.Full. Eclipse JDT.LS defaults to TextDocumentSyncKind.Incremental (kind 2), meaning contentChanges entries contain incremental edits with range + text (where text is only the replacement for that range, not the full document).

Root Cause

When jdtls returns incremental sync in its initialize response, the client sends incremental didChange notifications. Each contentChanges entry looks like:

{"range": {"start": {"line": 0, "character": 5}, "end": {"line": 0, "character": 5}}, "text": "x"}

At adapters/jdtls.py:60, the code does:

text = obj["params"]["contentChanges"][-1]["text"]
with open(self._jdtls_main_path, "w", encoding="utf-8") as f:
    f.write(text)

This writes only the delta (e.g., a single character "x") as the entire file content, overwriting the real document. This corrupts the on-disk file that jdtls reads, causing the language server to operate on incorrect source code.

Impact: Every keystroke after the first didOpen would corrupt the file on disk, making jdtls diagnostics, completions, and other features incorrect.

Prompt for agents
In adapters/jdtls.py, the didChange handler at lines 59-62 assumes contentChanges contains the full document text, but Eclipse JDT.LS uses incremental sync by default. To fix this, either:

1. Force full document sync by intercepting the initialize response in lsp_to_ws and overriding the textDocumentSync capability to Full (kind 1), so the client always sends full document content in didChange. This is the simplest fix.

2. Or, maintain the file content in memory and apply incremental changes (range-based text replacements) before writing the file. This is more complex but preserves incremental sync benefits.

Option 1 is recommended: in lsp_to_ws, parse the initialize response (look for id matching the initialize request), find result.capabilities.textDocumentSync and set it to 1 (Full) before forwarding to the client.
Open in Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

seems true, however it works regardless? is this a necessary fix? would add a lot of complication

lsp_server.py Outdated
allow_concurrent_inputs=20,
timeout=60 * 60,
)
@concurrent(max_inputs=20)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it's more conventional to do @modal.concurrent

lsp_server.py Outdated
Comment on lines +120 to +121
memory=1024,
cpu=1,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

don't think you need to specify this -- you should be able to just burst up to this amount

lsp_server.py Outdated
single_use_containers=True,
memory=1024,
cpu=1,
scaledown_window=60 * 60,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why is scaledown window so high? does this even do anything if single_use_containers is True? Also, why does it have to be a single use container?

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

TL;DR: good questions, set @modal.concurrent to 3 and remove the single_use_containers and scaledown_window arguments.

from what i read about JDTLS, i assumed that using one container per user would prevent indexing errors; that's why i recommended sahil include the single_use_containers and scaledown_window arguments. however, i just did some testing, and it seems to work alright with multiple users in one container (which makes sense because each user gets a new process upon connection, and each process has an isolated workspace). so, i would set @modal.concurrent to 3 and remove the single_use_containers and scaledown_window arguments.

if self._jdtls_main_path:
if method == "textDocument/didOpen":
text = obj["params"]["textDocument"]["text"]
with open(self._jdtls_main_path, "w", encoding="utf-8") as f:
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is there any reason why we actually need to write out the files to disk? ChatGPT claims that you don't need to do this for simple cases: https://chatgpt.com/share/69a5524c-8958-8008-a164-b6c26805f462 of course ChatGPT could be wrong though. I did not investigate myself

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fwiw I did not need to do this for C++ language server which is why I would be surprised if we had to do it for Java. (But I haven't investigated myself)

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

based on my own research of the docs and testing on my own, (see https://github.com/eclipse-jdtls/eclipse.jdt.ls/blob/main/README.md#running-from-the-command-line) it seems -data is required. if you know of a way to bypass it though, tell me and im happy to implement it.

Copy link
Member

@thecodingwizard thecodingwizard Mar 3, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Even if -data is required is it necessary to actually write out every keystroke to disk? i.e. if you got rid of the file writing portion does this still work?

(Presumably you would need the data folder to actually exist & have project setup files though; the only thing you would get rid of is actually writing the files to disk)

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wait great point, that would explain why things worked despite updating being basically broken. I can probably just create main. Java as an empty file or something, which means it could also lose its limit of 3 concurrency, simplifying the IDE code too since just one url. Will check if this is true tomorrow

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not even sure if creating Main.java is strictly needed (but maybe it is!)

I guess one intuition for why writing to file on every keystroke should not be necessary is IDEs do not write to file on every keystroke, but their intellisense works just fine

@aunncodes aunncodes marked this pull request as draft March 3, 2026 03:50
@aunncodes aunncodes marked this pull request as ready for review March 3, 2026 05:02
@aunncodes
Copy link
Author

Alright I made the basic changes in the main file, however it seems it is not possible to run jdtls without the files themselves to my knowledge, and the code detected by the bot although true, seems to work anyway and a solution would be unnecessary and very overcomplicated.

@aunncodes aunncodes marked this pull request as draft March 5, 2026 04:12
@aunncodes
Copy link
Author

You were completely correct @thecodingwizard , so I have removed a bunch of now unnecessary code. Also, since we no longer need to write code to the disk, I think we can also merge the 2 seperate modal servers into one. I really dont like how jdtls is alone just so it can have a different concurrency, and now that disk writing was made unnecessary, I don't see any other reason for seperate concurrency.

@aunncodes
Copy link
Author

Added one more commit that combines the project and data directory, because I realize there is no real reason for them to be two seperate paths

@aunncodes aunncodes marked this pull request as ready for review March 5, 2026 04:44
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants