Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

eclipse.jdt.ls support #112

Closed
tbo opened this issue Oct 6, 2018 · 59 comments
Closed

eclipse.jdt.ls support #112

tbo opened this issue Oct 6, 2018 · 59 comments
Labels
enhancement New feature or request

Comments

@tbo
Copy link

tbo commented Oct 6, 2018

I've been testing eclipse.jdt.ls to add support for Java. Most things work pretty well. The only major feature, that doesn't work is the resolution of external dependencies. eclipse.jdt.ls represents external resources by prefixing them with jdt://. Example:

jdt://contents/slf4j-api-1.7.10.jar/org.slf4j/Logger.class?=linkedgeodata-core/%5C/home%5C/someone%5C/.m2%5C/repository%5C/org%5C/slf4j%5C/slf4j-api%5C/1.7.10%5C/slf4j-api-1.7.10.jar%3Corg.slf4j(Logger.class

In order to resolve those URIs, eclipse.jdt.ls provides an additional LSP-Method: java/classFileContents. See here to get a complete list of eclipse.jdt.ls' LSP extensions.

Possible Solutions

Add a handler for jdt://-URIs, that passes them to java/classFileContents and show the response in a new readonly buffer. It could look like this:

au BufReadCmd,FileReadCmd,SourceCmd jdt://* call retrieveJdtAndShowInNewBuffer(expand("<amatch>"))

Keep in mind, that jdt buffers are still valid java files, which can contain additional references.

Most LSP-Clients use this approach:

- OR -

Integrate vscode-java.

My Configuration

coc-settings.json:

{
    "languageserver": {
        "java": {
            "command": "javalsp",
            "filetypes": ["java"],
            "initializationOptions": {
                "extendedClientCapabilities": {
                    "classFileContentsSupport": true
                }
            },
            "settings": {
            }
        }
    }
}

Without declaring the classFileContentsSupport eclipse.jdt.ls won't send jdt references.

My launcher (javalsp):

#!/bin/sh
export JAVA_HOME=$(/usr/libexec/java_home -v 1.8)
WORKSPACE_DIR=/Users/tbo/git 
PLATFORM=mac # win, linux, mac
JDT_DIR=~/git/eclipse.jdt.ls/org.eclipse.jdt.ls.product/target/repository

cd ${JDT_DIR}
LAUNCHER=`ls ${JDT_DIR}/plugins/org.eclipse.equinox.launcher_*`
java -Declipse.application=org.eclipse.jdt.ls.core.id1 -Dosgi.bundles.defaultStartLevel=4 -Declipse.product=org.eclipse.jdt.ls.core.product -Dlog.level=ALL -noverify -Xmx1G -jar ${LAUNCHER} -configuration ./config_mac -data ${WORKSPACE_DIR}

You'll have to update the settings depending on your own system. I would have put everything into the coc-settings.file, but I didn't know how to change into the directory of the language server before the command execution.

BTW: vscode-java already includes the installation of the LSP server.

@chemzqm chemzqm added the enhancement New feature or request label Oct 6, 2018
@chemzqm
Copy link
Member

chemzqm commented Oct 11, 2018

I've added the support for send request to custom language client, it could be done by something like:

au BufReadCmd,FileReadCmd,SourceCmd jdt://* call LoadJavaContent(expand("<amatch>"))

function! LoadJavaContent(uri)
  setfiletype java
  let content = CocRequest('java', 'java/classFileContents', {'uri': a:uri})
  call setline(1, split(content, "\n"))
  setl nomod
  setl readonly
endfunction

@chemzqm
Copy link
Member

chemzqm commented Oct 11, 2018

I didn't know how to change into the directory of the language server before the command execution.

You can create a .vim directory in your workspace root, coc would search the folder that contains .vim or .git from current file (or cwd if no file opened) and use that folder as cwd of language server command. You can also change cwd of command by set languageserver.java.cwd option in .vim/coc-settings.json.

vscode-java already includes the installation of the LSP server.

I've looked at the code of vscode-java, it should be easy to convert it to coc extension.

@tbo
Copy link
Author

tbo commented Oct 11, 2018

I wasn't able to get the snippet running, but I have an idea why it isn't working. It seems, that coc.nvim removes the protocol and the query string from the URI. So instead of:

jdt://contents/slf4j-api-1.7.10.jar/org.slf4j/Logger.class?=linkedgeodata-core/%5C/home%5C/someone%5C/.m2%5C/repository%5C/org%5C/slf4j%5C/slf4j-api%5C/1.7.10%5C/slf4j-api-1.7.10.jar%3Corg.slf4j(Logger.class

The buffer opens with:

/contents/slf4j-api-1.7.10.jar/org.slf4j/Logger.class

I tried to account for the missing protocol prefix by slightly modifying the snippet:

function! LoadJavaContent(uri)
    setfiletype java
    let content = CocRequest('java', 'java/classFileContents', {'uri': 'jdt:/' . a:uri})
    call setline(1, split(content, "\n"))
    setl nomod
    setl readonly
endfunction

autocmd! BufReadPre,BufReadCmd,FileReadCmd,SourceCmd *.class call LoadJavaContent(expand("<amatch>"))<CR>

But all this did, was open a buffer, that only contained null. So it seems, that the query string is required for java/classFileContents to resolve the class.

@chemzqm
Copy link
Member

chemzqm commented Oct 11, 2018

So it seems, that the query string is required for java/classFileContents to resolve the class.

It's a bug with jump to location which has custom shcema.
I've fixed that.

@tbo
Copy link
Author

tbo commented Oct 11, 2018

You mean "fix(workspace): jump to custom uri not works.", right? I tried it, but it didn't change anything for me. I'm still getting truncated URIs.

@chemzqm
Copy link
Member

chemzqm commented Oct 11, 2018

Yes, it should works, you need to rebuild coc.nvim by yarn install or just yarn build, if you have build directory inside root of coc, let g:coc_force_debug = 1 is required to make coc use the javascript code build by youself.

@tbo
Copy link
Author

tbo commented Oct 11, 2018

That did it. I did some testing and everything looks fine. Your original snippet works without any additional changes now.

Thanks!

@tbo
Copy link
Author

tbo commented Oct 11, 2018

I found another java related issue. I get the following error message after selecting a code action ("coc-codeaction"):

[coc.nvim] Command: java.apply.workspaceEdit not found

@chemzqm
Copy link
Member

chemzqm commented Oct 11, 2018

I've looked at the code of vsocde-java, that command have to be registered on client side.
A coc extension of java could help.

@tbo
Copy link
Author

tbo commented Oct 12, 2018

It seems, that coc.nvim is in good company: eclipse-jdtls/eclipse.jdt.ls#376. LanguageClient-neovim had to implement a workaround as well.
IMHO a coc extension of vscode-java would probably be the cleanest solution.

@chemzqm
Copy link
Member

chemzqm commented Oct 15, 2018

There is https://github.com/neoclide/coc-java extension now, which support configuration, commands and snippets.

@tbo
Copy link
Author

tbo commented Oct 15, 2018

Awesome work! I did some initial testing and noticed two issues:

  • jdt-URIs cannot be resolved anymore. I tested with and without your LoadJavaContent-Snippet.
  • The current working directory isn't properly set, which leads to this error message: [coc.nvim] JDT Language Server error Failed to scan /Users/someone). I'm using vim-rooter to set the cwd automatically, when opening files. I have to restart coc or set the cwd manually upfront to get it working.

@chemzqm
Copy link
Member

chemzqm commented Oct 16, 2018

  • I've fixed the issue of handle jdt uri.
  • How to trigger the error message [coc.nvim] JDT Language Server error Failed to scan /Users/someone ? It that happens just after server started?

@chemzqm
Copy link
Member

chemzqm commented Oct 16, 2018

I've improved the root logic to support root patterns same as vim rooter.
You can create a .vim or .git folder in your project root to solve the problem without update coc.

@tbo
Copy link
Author

tbo commented Oct 16, 2018

I can confirm, that jdt-URIs now work. Will you include the snippet in coc.nvim?

I deactivated vim-rooter and tested the root pattern feature:

  • The current working directory isn't updated. It still points to my home directory.
  • I don't get the error message anymore
  • coc-definition can only resolve symbols in the same file. Code actions also don't work properly.
  • If I restart coc.nvim, then everything works as expected, but the current working directory still points to my home directory.

@chemzqm
Copy link
Member

chemzqm commented Oct 17, 2018

Will you include the snippet in coc.nvim?

No, but I will make coc expose API for extension to handle custom schema of uri just like VSCode.

I deactivated vim-rooter and tested the root pattern feature

No need to deactivated vim-rooter, it looks like you're using binary version of coc, to make sure use the javascript code build by yarn install, add let g:coc_force_debug = 1 to your init.vim

@tbo
Copy link
Author

tbo commented Oct 17, 2018

No need to deactivated vim-rooter, it looks like you're using binary version of coc, to make sure use the javascript code build by yarn install, add let g:coc_force_debug = 1 to your init.vim

I rechecked everything. I'm pretty sure, that I'm using the source version.

I can create a minimal nvim config and a java test project, if you want?

@chemzqm
Copy link
Member

chemzqm commented Oct 17, 2018

I think I've got the reason that the rootPath could be wrong, the extension could be activited on filetype change when the buffer is not loaded, so root can't be resolved from current buffer.

I don't get the problem because my java home is resolved from system and it takes more time.

@tbo
Copy link
Author

tbo commented Oct 18, 2018

Is there anything I can help with?

@chemzqm
Copy link
Member

chemzqm commented Oct 19, 2018

I've fixed the issue, it should works now.

@tbo
Copy link
Author

tbo commented Oct 21, 2018

I can confirm that. I went ahead and tested the example vim config from top to bottom. I found only one remaining issue. It seems coc-implentation isn't able to find implementations. I cross-checked the same project with vscode and there it is possible to resolve implementations. I also checked the java lsp server output. jdt seems to receive the method call for textDocument/implementation and doesn't output any errors. The "Go To Implementation"-feature is quite fresh. Maybe coc-java is using an older version of vscode-java?

I also checked the java.show.implementations via Denite coc-command, but I only get this error message:

[coc.nvim] Command error: Cannot read property 'map' of undefined

I don't know if this issue is actually related to the coc-implementation issue.

@chemzqm
Copy link
Member

chemzqm commented Oct 21, 2018

I also checked the java.show.implementations via Denite coc-command

It's a internal command, so it should not invoked directly.

I cross-checked the same project with vscode and there it is possible to resolve implementations.

For the same file and same location? I've checked goto implementation and the server returns empty location list.

@chemzqm
Copy link
Member

chemzqm commented Oct 21, 2018

The argument for goto implementations send to server is wrong, I just fixed that, I've tested it working now.

@tbo
Copy link
Author

tbo commented Oct 23, 2018

Ok, that now works as well. I continued testing and noticed, that Code Actions sometimes throw errors: [coc.nvim] applyEdit error: nvim_buf_set_lines: Argument "start" is higher than "end"

Example: Executing an "import" code action on @Entity should change this

import java.util.ArrayList;
import java.util.List;

@Entity
public class TestClass {

to this:

import java.util.ArrayList;
import java.util.List;

import javax.persistence.Entity

@Entity
public class TestClass {

But instead the error message is being displayed.

@chemzqm
Copy link
Member

chemzqm commented Oct 23, 2018

There's a bug with diffLines, will be fixed in next release.

@chemzqm
Copy link
Member

chemzqm commented Oct 24, 2018

Code Actions sometimes throw errors:

Fixed in 0.0.28.

Latest coc-java could handle jdt uri itself, the autocmd for FileReadCmd no longer required.

@tbo
Copy link
Author

tbo commented Oct 26, 2018

I found two additional issues:

  1. Sometimes invalid completions items are shown:

screen shot 2018-10-26 at 14 36 55

It should look like this:

screen shot 2018-10-26 at 14 37 19

  1. The order of completion items is weird:

screen shot 2018-10-26 at 14 33 49

The vscode completion for comparison:

screen shot 2018-10-26 at 14 56 15

@chemzqm
Copy link
Member

chemzqm commented Oct 26, 2018

Sometimes invalid completions items are shown:

Possible caused by slow of content synchronize, Try change coc.preferences.triggerCompletionWait in coc-settings.json, which default to 60 in miliseconds.

The order of completion items is weird:

Looks like that VSCode is giving higher priority to variable items, but coc simply use fuzzaldrin for sort items. It could be improved.

@tbo
Copy link
Author

tbo commented Oct 26, 2018

Possible caused by slow of content synchronize, Try change coc.preferences.triggerCompletionWait in coc-settings.json, which default to 60 in miliseconds.

I tried it with four different values: 180, 300, 600, 1000. Up to 300 the issue persisted and after 600 I got no completions at all.
I doubt, that this is a timing issue. The issue only occurs, if I write the symbol "by hand". If I write "sta" and select the complete symbol from the completion list, then everything works as expected. It looks like it doesn't notice, that the symbol is "done". Check out the position of the completion window. It still sits below "stats", but it should start after the dot.

Looks like that VSCode is giving higher priority to variable items, but coc simply use fuzzaldrin for sort items. It could be improved.

It would already help, if fuzzaldrin would favour case sensitive matches. There already is an issue for this, but nobody touched it for over three years.

Is it actually necessary to sort the completion list? Could it be, that the extension already returns a properly sorted list? It seems, that vscode also includes the proximity of all symbols into the scoring. Otherwise cal and not calendar would be at the top.

@tbo
Copy link
Author

tbo commented Oct 26, 2018

I've changed to keep the sortText on increment filter, the result seems better.

Indeed, it is much better now.

I've got the reason, that's because coc prefer label as filterText, but it should prefer insertText, it's fixed on master branch.

It didn't fix the issue for me. I did some further testing and noticed, that I get different results for different variable names:

kapture 2018-10-26 at 20 17 05

I used the official aws java sample for this test.

@chemzqm
Copy link
Member

chemzqm commented Oct 26, 2018

It didn't fix the issue for me. I did some further testing and noticed, that I get different results for different variable names:

I've changed the completion to trigger rather than filter the text on increment filter, it should works as expected now.

@tbo
Copy link
Author

tbo commented Oct 28, 2018

I found another issue. The package name completion creates invalid values:

kapture 2018-10-28 at 10 33 22

I guess this is actually an issue in eclipse.jdt.ls, since LanguageClient-neovim has the same problems. I think jdt should only return partial results. @chemzqm can you confirm this?

Other than that everything works very well!

@chemzqm
Copy link
Member

chemzqm commented Oct 28, 2018

There's a trick on VSCode, it won't trigger completion if it found strict match on first complete item. Coc could do the same.

@chemzqm
Copy link
Member

chemzqm commented Oct 28, 2018

I've implemented that on master branch.

@tbo
Copy link
Author

tbo commented Oct 28, 2018

I checked out master, but it didn't fix the issue for me.

There's a trick on VSCode, it won't trigger completion if it found strict match on first complete item.

I don't think, that this explains the issue. Every item on the completion list shows the same erroneous behavior. They always show the complete package path and should completely replace the first symbol:

  1. Write package com.
  2. Completion list popups: com.amazonaws.samples, com.sun.org.omg.CORBA, ...
  3. Select the first one
  4. Final output package com.amazonaws.samples thus replacing com. with com.amazonaws.samples

Possible solutions:

  • Replace the first symbol with the complete path:
    com.amazonwas.samples replaces com.
  • Only show partials in completion list. Instead of com.amazonwas.samples show amazonaws.samples and append amazonwas.samples to com.

@chemzqm
Copy link
Member

chemzqm commented Oct 28, 2018

It fixed the issue when you type . while pumvisible, but not type . without pumvisible.

I've got the reason, the language server return textEdit and change the complete item to snippet kind on complete resolve, so VSCode just use the textEdit, you can do the same in coc by just confirm the completion with <C-y> (by default), but I can't update popup menu on completion resolve to let user know the item is changed to snippet.

@chemzqm
Copy link
Member

chemzqm commented Oct 28, 2018

I've fixed the position of module import at neoclide/coc-java@8ce7341.

Master of coc.nvim and latest coc-java required.

@tbo
Copy link
Author

tbo commented Oct 28, 2018

That works! I've got no more complaints.
@chemzqm Thanks for all the time you put into this issue!

@tbo tbo closed this as completed Oct 28, 2018
@ttiurani
Copy link

Thanks a lot for this feature, it's exactly what I've been looking for for ages! Also loving coc.nvim so far.

I've been playing around with this and have a dumb question: how do you add on import to a class? I typed:

private Session session

Then I should do something on top of Session that would give me suggestions about which of the many x.y.z.Session imports I should use.

After code completion, this is the second most used thing in Java development so surely there must be a way to do this with eclipse.jdt.ls? Came up pretty much empty when googling as there's only "Organize imports" (What exactly do I need to do to call that via coc.nvim?), which to me seems like it does stuff for the whole file and can't possibly select the right Session above there. @chemzqm

@tbo
Copy link
Author

tbo commented Oct 29, 2018

@ttiurani I think you are searching for codeaction. Check the example configuration:

Excerpt:

" Remap for do codeAction of selected region, ex: `<leader>aap` for current paragraph
vmap <leader>a  <Plug>(coc-codeaction-selected)
nmap <leader>a  <Plug>(coc-codeaction-selected)

" Remap for do codeAction of current line
nmap <leader>ac  <Plug>(coc-codeaction)

@ttiurani
Copy link

ttiurani commented Oct 31, 2018

@tbo thanks! I got the bottom one, <Leader>ac to work.

I'm just so very bad with vimscript that I can't figure out what exactly I'm suppossed to press to invoke those upper ones for a single word. If I have the cursor on top that Session word, which has a compilation problem, what exactly would I press, if my leader is space? I also tried to just visually select that word and invoke :<Plug>(coc-codeaction-selected) but I get some odd "E488: Trailing characters" warning.

@chemzqm
Copy link
Member

chemzqm commented Oct 31, 2018

I also tried to just visually select that word and invoke :<Plug>(coc-codeaction-selected)

You can't invoke <Plug> command in vim directly. Add

 vmap <leader>a  <Plug>(coc-codeaction-selected)

to your init.vim or .vimrc

In normal mode, select the word by viw then type <leader>a to get the code action list.

@ttiurani
Copy link

Thanks, now it works!

My problem was that I thought about being clever and using nnoremap and vnoremap as opposed to nmap and vmap. I triple checked that I don't have <leader>a mapped to anything, but still that didn't work.

@chemzqm
Copy link
Member

chemzqm commented Oct 31, 2018

nnoremap and vnoremap would never works with keymap starts with <Plug> since <Plug> needs to be remapped.

@danmikita
Copy link

@tbo @chemzqm First of all - thank you for working so hard on this, it is the easiest and most performant solution for java in nvim.

Second - I am having trouble with the language server finding generated sources. Previously I compiled eclipse.jdt.ls myself and used the standard set-up with LanguageServer-neovim. When set up that way, it discovered generated sources in my project just fine. I don't have any configurations set in my CoCConfig right now, is there something I am missing that is needed for generated sources to be found in my multi-module maven project?

@danmikita
Copy link

I have done further debugging and thinking on this issue. I don't believe it is a generated sources problem since the sources that are not being found are compiled into a JAR and placed in my local .m2 directory. I am then referencing that dependency in my other module. It is almost like the Language server can't see my local .m2 repo - but then all of my other dependencies should be failing too. I'm not sure at this point what's happening. I have set the configuration to use my local maven settings.xml.

Perhaps does the Eclipse language server use it's own .m2 location?

@chemzqm
Copy link
Member

chemzqm commented Nov 28, 2018

I think eclipse.jdt.ls use workspaceFolders to find addtional source code, but it's not supported by coc-java.
The problem is how to find those source locations? I'm not familiar with java.

@chemzqm
Copy link
Member

chemzqm commented Nov 28, 2018

It it possible to add those source code folders as params of command that start eclipse.jdt.ls?

@tbo
Copy link
Author

tbo commented Nov 28, 2018

@danmikita The support for ~/.m2/settings.xml is quite new:

eclipse-jdtls/eclipse.jdt.ls#849

Maybe it is working with LanguageServer-neovim, because you used the latest development version? A recent comment from one of the core developers indicates, that ~/.m2 wasn't supported:

redhat-developer/vscode-java#702 (comment)

@danmikita
Copy link

danmikita commented Nov 28, 2018

@tbo Sure enough! I checked out master 12 days ago which had the commit for that feature. So - it looks like that feature was released in version 0.28.0 just a few days ago. Is there a way to point coc-java to use that new tag?

@chemzqm
Copy link
Member

chemzqm commented Nov 28, 2018

Is there a way to point coc-java to use that new tag?

No, coc-java bundled with release of eclipse.jdt.ls.
you can start eclipse.jdt.ls in socket mode, and add set environment variable JDTLS_CLIENT_PORT to port number, then coc-java would connect to server.

I can add an option to coc-java for custom server home.

@chemzqm
Copy link
Member

chemzqm commented Nov 28, 2018

coc-java 1.1.6 added java.jdt.ls.home for custom server home.

@danmikita
Copy link

Wow - you work fast! I tried to get it working, but am getting an error while trying to start the server. These are the steps I took:

  1. :CocUpdate
  2. Edit coc-settings.json: "java.jdt.ls.home": "/Users/<username>/git/eclipse.jdt.ls/org.eclipse.jdt.ls.product/target/repository/"
  3. Open a file.
  4. Error: at Process.ChildProcess._handle.onexit (internal/child_process.js:198:12)

I haven't dug into your code at all - so I admit this is the lazy approach... But when using LanguageClient-neovim I needed to use a small bash script to start the server.

#!/usr/bin/env sh

server='/Users/<username>/git'

java \
    -Declipse.application=org.eclipse.jdt.ls.core.id1 \
    -Dosgi.bundles.defaultStartLevel=4 \
    -Declipse.product=org.eclipse.jdt.ls.core.product \
    -noverify \
    -Xms1G \
    -jar $server/eclipse.jdt.ls/org.eclipse.jdt.ls.product/target/repository/plugins/org.eclipse.equinox.launcher_1.*.jar \
    -configuration $server/eclipse.jdt.ls/org.eclipse.jdt.ls.product/target/repository/config_mac/ \
    "$@"

Here I had to set several arguments and even reference a specific configuration file.

My open questions:

  • Did I actually update to your latest version? (using :CocUpdate)
  • Am I pointing to the correct folder in the eclipse.jdt.ls directory?

Thank you guys again for helping! This is already amazing! This is the only missing piece for me.

@chemzqm
Copy link
Member

chemzqm commented Nov 28, 2018

Did I actually update to your latest version? (using :CocUpdate)

If you can add "java.jdt.ls.home" without warning from json server, then you're using new version.

Am I pointing to the correct folder in the eclipse.jdt.ls directory?

The directory should be correct, it contains plugins and config_mac folder, try remove / at the end.

@danmikita
Copy link

danmikita commented Nov 28, 2018

Okay - I'm about to leave for the day, so this is my last update. I tried various permutations and couldn't get my locally compiled server to start up with your change. So I decided to revert back to no settings at all so I could get some work done. :)

Unfortunately, the default server will no longer start. I am now getting a java.lang.NoClassDefFoundError: org/eclipse/core/resources/ResourcesPlugin error in the server log.

Not sure what to do at this point - if I don't hear back, I'll poke around again for a bit tomorrow.

Thanks again for your help!

@danmikita
Copy link

Quick update:
Fixed the issue I was having yesterday by changing my JAVA_HOME to be JDK 11. Previously I was set to JDK 8. I also noticed in the eclipse language server readme that the lsp only works with JDK 9+.

https://github.com/eclipse/eclipse.jdt.ls/blame/master/README.md#L29

Unfortunately, the maven settings is still not working...but I'm sure we'll get there. Thanks again for your help!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
None yet
Development

No branches or pull requests

4 participants