GroovyLanguageServer / groovy-language-server

A language server for Groovy — designed for Moonshine IDE, but may be useful in other environments
https://moonshine-ide.com/
Apache License 2.0
185 stars 59 forks source link

New Feature: Add support for Gradle based groovy projects. #26

Open rohitbishnoi opened 4 years ago

reality commented 4 years ago

So, what would this involve exactly? Would it just be a matter of parsing the gradle file for the targets/libraries to consume? I think Java language servers already can do this... I might take a look to see if it can be borrowed...

joshtynjala commented 4 years ago

The JDT language server is based on Eclipse and says it uses Buildship for Gradle support. Not sure how easy that would be to integrate outside of an Eclipse-based environment.

https://github.com/eclipse/eclipse.jdt.ls

joshzcold commented 3 years ago

What would be the work around for this before this feature is done? say I have all my gradle dependencies in ~/.gradle/caches/modules-2/files-2.1/ Can I manually configure the class path with a glob? Like ~/.gradle/caches/modules-2/files-2.1/**/*

Update: I realize that if I want this ill have to add it as a feature to https://github.com/dansomething/coc-groovy which uses this server.

shaneikennedy commented 5 months ago

Based on the solution implemented in the referenced PR by @joshzcold (https://github.com/dansomething/coc-groovy/pull/12), it seems like the challenging part was to get the classpath but from what I understand you can get the classpath via gradlew with:

./gradlew dependencies --configuration compileClasspath

or for the runtime classpath:

./gradlew dependencies --configuration runtimeClasspath

That being said, in typical gradle fashion it only outputs in a pretty human readable way so parsing this might be annoying

joshzcold commented 5 months ago

@shaneikennedy the maintainer of coc-groovy made a script to handle that for the same reason

https://github.com/dansomething/coc-groovy/tree/main/utils%2Fgradle-classpath%2Fbin

He references the built jar of another one of his projects

https://github.com/dansomething/gradle-classpath

shaneikennedy commented 5 months ago

@joshzcold I read the PR but didn't realize that script became it's own repo :) thanks!

gtnbssn commented 2 months ago

I have been trying to setup groovyls in neovim: https://www.reddit.com/r/groovy/comments/1eruc6o/setting_up_groovyls_in_neovim_to_properly_find/

I got neovim to recognise the standard groovy libraries by adding this line to my config:

/Users/[my user name]/.asdf/installs/groovy/4.0.9/lib/

(It wasn't working when I was using ~/ instead of the developed form.)

But I cannot get it to find the dependencies gradle has installed, unless i write the path all the way to the jar. And so it seems I would need to write the path of every single dependency installed in gradle, which is definitely not great.

It does not seem possible to use a glob as mentioned by @joshzcold 3 years ago.

Is there a solution to get the language server to find the gradle dependencies?

joshzcold commented 2 months ago

@gtnbssn your comment gave me some inspiration to get this working atleast in neovim before tackling the lsp server itself.

groovy language server currently doesn't have any support for figuring out classpath for maven or gradle dynamically.

Maybe maven puts its dependencies in a generic place that groovy lsp can pick up?

In neovim for gradle support you would need to do the following.

  1. Install https://github.com/dansomething/gradle-classpath
cd /opt
sudo wget https://github.com/dansomething/gradle-classpath/releases/download/1.3.0/gradle-classpath-1.3.0.zip

sudo unzip gradle-classpath-1.3.0.zip && sudo rm gradle-classpath-1.3.0.zip

cd /usr/local/bin
sudo ln -s /opt/gradle-classpath-1.3.0/bin/gradle-classpath
  1. Setup neovim to execute gradle-classpath and set the classpath after attach

local function set_groovy_classpath() local Job = require("plenary.job") local gradle_dir = vim.fs.dirname(search_up("build.gradle")) vim.notify("groovyls: starting gradle dependencies install at: " .. gradle_dir, vim.log.levels.INFO) Job :new({ command = "./gradlew", args = { "dependencies", }, cwd = gradle_dir, onexit = function(j, ) vim.schedule(function() if j.code ~= 0 then vim.notify( "./gradlew dependencies: " .. vim.inspect(j._stderr_results), vim.log.levels.ERROR ) else Job :new({ command = "gradle-classpath", cwd = gradle_dir, onexit = function(k, ) vim.schedule(function() if k.code ~= 0 then vim.notify("gradle-classpath: " .. vim.inspect(k._stderr_results), vim.log.levels.ERROR) else local _classpath_results_stdout = k._stdout_results[1] local classpath_results = vim.split(_classpath_results_stdout, ":") vim.notify("Setting classpath in groovyls ...", vim.log.levels.INFO) local groovy_lsp_client = vim.lsp.get_clients({ name = "groovyls" })[1] if not groovy_lsp_client then vim.notify("Error lsp client groovyls not found.", vim.log.levels.ERROR) return end if groovy_lsp_client.settings then groovy_lsp_client.settings = vim.tbl_deep_extend( "force", groovy_lsp_client.settings, { groovy = { classpath = classpath_results } } ) else groovy_lsp_client.config.settings = vim.tbl_deep_extend( "force", groovy_lsp_client.config.settings, { groovy = { classpath = classpath_results } } ) end groovy_lsp_client.notify( "workspace/didChangeConfiguration", { settings = groovy_lsp_client.settings } ) end end) end, }) :start() end end) end, }) :start() end


- call the `set_groovy_classpath` function in `on_attach` when you are setting up your lsp server

```lua

["groovyls"] = function()
    lspconfig.groovyls.setup({
        on_attach = function(client, bufnr)
            on_attach(client, bufnr)
            set_groovy_classpath()
        end,
    })
end,

It takes a while for groovy-ls to initialize before I can start using it, but I was able to get my gradle dependencies with this method.

plenary is required for this to work, but you probably already have that plugin

Results image

build.gradle image

Make sure you look at the lsp logs for errors while you are debugging any issues

truncate -s 0G ~/.local/state/nvim/lsp.log && tail -f ~/.local/state/nvim/lsp.log
joshzcold commented 2 months ago

I believe here is where groovy-language-server gets most of its built-in classes, otherwise its up to parents to provide the additional classpaths

https://github.com/GroovyLanguageServer/groovy-language-server/blob/7be0244a1a58a144c382ee95a22fcc7ce9662706/src/main/java/net/prominic/groovyls/config/CompilationUnitFactory.java#L74

joshzcold commented 2 months ago

These features might be the ultimate solution

https://github.com/eclipse-jdtls/eclipse.jdt.ls/issues/491 https://github.com/groovy/groovy-eclipse/issues/811

eclipse.jdt.ls already supports gradle and maven builds and has way more LSP support built out than groovy-language-server. Its just getting maintainers to dedicate time to groovy seems to be the issue.

gtnbssn commented 2 months ago

Wow, I wasn't expecting such a quick and detailed answer on this issue! Thank you so much!

I tried your suggestion, however the gradle-classpath script takes a whole 3 minutes each time on my m2air.

I will stick to my dirty DIY solution for now, or just use intelliJ. Curious to see if a more ideal solution for neovim comes along one day!