lsp-java
lsp-java copied to clipboard
More detailed doc on project convention
I have encountered a common issue that lsp-mode couldn't resolve java.lang.String(and thus no string method after pressing tab after String. )(actually couldn't resolve reference from any library at all)
I have resolved the issue by creating a gradle project. Everything works fine with java file inside a gradle project.
It seems that lsp-mode with java don't recognize liberies(like java.lang.String) if it's simply a
└── src
└── a
└── b
└── c
└── d
├── classA.java
├── classB.java
├── classC.java
├── Main.java
└── classD.java
structrued project directory. Rather, it work on a gradle-convention project structure(, or plus maven? I haven't tried and don't really know what maven is).
I think it'll reduce confusion(especially to newbies like me who aren't familiary with build tools either prefer using one) to add this note somewhere eye-catching in the readme doc, like "support and only support gradle and maven project" or stuff like that.
p.s.: and it will be better to have way to let lsp-mode find the libraries without project conventions== like with a variable like lsp-java-classpath
something?
I've been looking around to figure out how to configure things... unfortunately, lsp-java provides zero support for it and leaves it up to the Eclipse Server to sort things out... which is why it works on Maven/Gradle, as the Server then automatically keeps its internal configuration in sync with the POM or build.gradle files...
If you want to use a project without build tools (like I do - for the reason I am actually writing one myself) then the only way to configure anything seems to be to generate Eclipse config files, .classpath
and .project
. I am still trying to figure it out but it looks like lsp-java doesn't get in the way, at least, so you can start by actually configuring the project in Eclipse, then using the project from emacs... hopefully, the Eclipse file can be generated (lsp-java should be doing that already?!) so it's possible to configure thing from emacs.
Manually configuring a project, which is possible in any IDE, should be an easy thing to do :( Here's the VS Code Guide: https://code.visualstudio.com/docs/java/java-project#_configure-classpath-for-unmanaged-folders
Really well documented and has support for most things (though they don't seem to have the concept of separate classpaths for tests which is very annoying - but that may be an Eclipse limitation - VSCode also uses the Eclipse Server). It's unclear to me whether lsp-java can use the vscode settings.json config, it does have lsp-load-vscode-workspace
but I tried it and it just doesn't seem to do anything?
@renatoathaydes lsp-mode atm does not support settings.json. The settings are exposed as defcustoms in lsp-java
group. If a certain property is missing it can be easily exposed.
@yyoncho what I would like to see is a way to configure:
- where the sources are located (multiple directories)
- where "resources" (files to NOT compile, but put in the output jar) are located
- where test tests are located
- directory containing jars to add to the compile classpath
- same for tests as well.
Currently, it's impossible to configure any of this.
- where the sources are located (multiple directories)
So in https://code.visualstudio.com/docs/java/java-project#_configure-classpath-for-unmanaged-folders there is a setting "java.project.referencedLibraries"
- that corresponds to lsp-java-project-referenced-libraries
. Other settings should be either already mapped or in general it is easy to expose it or set it directly via lsp-register-custom-settings
which also accepts literals.
This setting is only useful if it is per project. Even a single "project" may have multiple modules. Are we supposed to use directory-local variables for this (I would think that's pretty inconvenient)?
Also, could you please try to find the settings for the bullet points I mentioned above? The VSCode name for the first one is java.project.sourcePaths
but I can't find anything like that in emacs.
This setting is only useful if it is per project. Even a single "project" may have multiple modules.
I doubt that it works that way in vscode. AFAICS in vscode example, these settings are global for the workspace. dir-local
won't work because jdtls is started one for all projects and it is the same in vscode.
Also, could you please try to find the settings for the bullet points I mentioned above? The VSCode name for the first one is
java.project.sourcePaths
but I can't find anything like that in emacs.
If something is missing then you can use lsp-register-custom-settings
like this: https://github.com/emacs-lsp/lsp-mode/blob/4688de7f33acde41adb9b79bc9a631599ff6a92b/test/lsp-common-test.el#L119. I mentioned that in the previous comment.
What I don't understand is what good is it to set a property for VS Code workspaces?? I guess the implicit assumption is that the Java LSP Server "understand" the VS Code settings?
I doubt that it works that way in vscode.
What do you mean? VSCode always loads a single workspace at a time and you can't work on multiple ones like you can in emacs. So, VS Code doesn't have global settings at all because each Java workspace will have its settings, and you can't re-use that across different workspaces (which is good). But the way you're saying LSP works in emacs, you set values for settings globally, which is pretty much useless - every project is different.
The way it should work is that, when emacs opens a Java file, which should trigger loading the Java project (or workspace), it should look for the root of the project, which is normally marked in one or more of the following ways:
- it has as
pom.xml
file - Maven projects. - it has a
settings.gradle
file - Gradle projects. - it has a
build.xml
file - Ant projects. - it has Eclipse files (
.project
and.classpath
). - it has VSCode files (
.vscode/settings.json
). - it has IntelliJ files (
*.iml
).
Any of these files above can be used to describe a Java project's structure, which normarlly consists of at least:
- location of source code (multiple directories).
- location of libraries (jars or directories containing jars).
- location of test code (multiple directories).
- location of test libraries.
- JDK version used.
A richer model would support modules as well, like IntelliJ does, but I believe VSCode doesn't. At least in my projects, I always have several sub-modules and it's very hard to configure that properly except for in IntelliJ, unfortunately.
But I really don't understand how emacs can do any of this by using things like lsp-java-project-referenced-libraries
which are not scoped to a project. What's the reasoning behind that? That people only work on one project?
What do you mean? VSCode always loads a single workspace at a time and you can't work on multiple ones like you can in emacs.
Emacs works like vscode - it has one active server and one active workspace (session). If you open another project it will be added to the active session (workspace in vscode). Emacs will start only once instance of jdtls for one session just like vscode does. The difference comes from the fact that emacs manages the workspace folders implicitly while it is explicit in vscode.
But I really don't understand how emacs can do any of this by using things like lsp-java-project-referenced-libraries which are not scoped to a project.
Scoping settings in emacs is performed via .dir-locals
. If you want to work on project A and project B then you should make sure that dir locals are loaded before starting the server and that you have 2 different sessions (here you can use lsp-load-vscode-workspace )
@yyoncho I couldn't get this working... I tried the following:
(lsp-register-custom-settings '(("java.project.sourcePaths" "/Users/renato/some-project/src")))
I then ran lsp-workspace-restart
. But that dir was not added to the sources...
Looking around, I found out that I can change the sources at ~/.emacs.d/workspace/some-project<random-id>/.classpath
.
Something like this:
<?xml version="1.0" encoding="UTF-8"?>
<classpath>
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/>
<classpathentry kind="src" path="_/src/main/java"/>
<!-- ADDED THIS LINE MANUALLY -->
<classpathentry kind="src" path="_/custom/src"/>
<classpathentry kind="output" path="bin"/>
</classpath>
After adding the source this way and running lsp-workspace-restart
, it did work.
This .classpath
file is auto-generated by emacs somehow... would be nice to understand how that works. If there was a way to tell emacs what to customize on this file that actually worked and was easy to do per-project, it would make the experience much nicer.
(lsp-register-custom-settings '(("java.project.sourcePaths" "/Users/renato/some-project/src")))
Source paths an array: https://github.com/redhat-developer/vscode-java/blob/master/package.json#L475
So it should be something like (lsp-register-custom-settings '(("java.project.sourcePaths" ["/Users/renato/some-project/src"])))
This .classpath file is auto-generated by emacs somehow... would be nice to understand how that works. If there was a way to tell emacs what to customize on this file that actually worked and was easy to do per-project, it would make the experience much nicer.
It is generated by the language server and IMHO the variables that you are using are the ones to control its generation.