lsp-java icon indicating copy to clipboard operation
lsp-java copied to clipboard

More detailed doc on project convention

Open hermanhel opened this issue 2 years ago • 12 comments

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?

hermanhel avatar Nov 25 '22 16:11 hermanhel

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.

renatoathaydes avatar Dec 28 '22 18:12 renatoathaydes

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 avatar Dec 28 '22 19:12 renatoathaydes

@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 avatar Dec 28 '22 19:12 yyoncho

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

renatoathaydes avatar Dec 28 '22 19:12 renatoathaydes

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

yyoncho avatar Dec 28 '22 19:12 yyoncho

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.

renatoathaydes avatar Dec 28 '22 20:12 renatoathaydes

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.

yyoncho avatar Dec 29 '22 07:12 yyoncho

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.

renatoathaydes avatar Dec 29 '22 11:12 renatoathaydes

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?

renatoathaydes avatar Dec 29 '22 11:12 renatoathaydes

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 avatar Dec 29 '22 11:12 yyoncho

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

renatoathaydes avatar Jan 06 '23 18:01 renatoathaydes

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

yyoncho avatar Jan 06 '23 19:01 yyoncho