summaryrefslogtreecommitdiffstats
path: root/after/ftplugin/java.lua
blob: 9401ec1dc23a9e2e38b8d3df2ba8e91e8198f610 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
-- FIXME: the following error is emmitted when starting up jdtls:
-- ERROR No LSP client found that supports vscode.java.resolveMainClass

local map, auto = core.misc.map, core.misc.auto

local ok, jdtls = pcall(require, "jdtls")
if not ok then
  vim.notify("jdtls not loaded, can't setup jdtls lsp or dap",
    vim.log.levels.INFO, {})
  return
end

local jdtls_install = core.mason.get_pkg_path("jdtls")
local java_dap_install = core.mason.get_pkg_path("java-debug-adapter")

-- make sure to check if things with 💀 need updating
local config = {
  cmd = {
    "/usr/lib/jvm/openjdk21/bin/java", -- 💀
    "-jar", -- 💀
    vim.fn.glob(vim.fs.joinpath(jdtls_install, "plugins/org.eclipse.equinox.launcher_*.jar")),
    "-configuration", jdtls_install.."config_linux",
    "-data", vim.fn.stdpath("cache").."/nvim-jdtls",

    "--add-modules=ALL-SYSTEM",
    "--add-opens", "java.base/java.lang=ALL-UNNAMED",
    "--add-opens", "java.base/java.util=ALL-UNNAMED",
    "-Declipse.application=org.eclipse.jdt.ls.core.id1",
    "-Declipse.product=org.eclipse.jdt.ls.core.product",
    "-Dlog.level=ALL",
    "-Dlog.protocol=true",
    "-Dosgi.bundles.defaultStartLevel=4",
    "-Xmx1G"
  },

  root_dir = vim.fs.dirname(vim.fs.find({
    "gradlew",
    ".git",
    "mvnw",
    "settings.gradle",     -- Gradle (multi-project)
    "settings.gradle.kts", -- Gradle (multi-project)
    "build.xml",           -- Ant
    "pom.xml"              -- Maven
  }, { upward = true })[1]),

  on_attach = function(_, bufnr)
    jdtls.setup.add_commands()

    -- add some jdtls specific mappings
    local opts = { buffer = bufnr }
    map("n", "cri", jdtls.organize_imports, opts)
    map("n", "crv", jdtls.extract_variable, opts)
    map("n", "crc", jdtls.extract_constant, opts)
    map("x", "crv", function() jdtls.extract_variable(true) end, opts)
    map("x", "crc", function() jdtls.extract_constant(true) end, opts)
    map("x", "crm", function() jdtls.extract_method(true) end, opts)

    -- do some refreshes often because I don't trust jdtls
    pcall(vim.lsp.codelens.refresh)
    auto("BufWritePost", {
      buffer = bufnr,
      desc = "refresh codelens",
      callback = function()
        pcall(vim.lsp.codelens.refresh)
      end
    })

    -- setup nvim-dap
    local ok, dap = pcall(require, "dap")
    if not ok then
      vim.notify("dap not loaded can't setup dap for jdtls",
        vim.log.levels.INFO, {})
      return
    end

    dap.adapters.java = nil -- remove any old java adapters
    jdtls.setup_dap({ hotcodereplace = "auto" })
    require("jdtls.dap").setup_dap_main_class_configs()
  end,

  -- don"t print out status messages
  handlers = {
    ["language/status"] = function() end
  },

  init_options = {
    bundles = {
      vim.fs.joinpath(java_dap_install,
        vim.fn.glob("extension/server/com.microsoft.java.debug.plugin-*.jar",
          true)
      )
    }
  }
}

-- generate the path to the java file(s)
---@type string?
local cache_path = vim.fs.joinpath(vim.fn.stdpath("cache"),
  "/JavaVersion.class")
---@type string?
local src_path = vim.fs.joinpath(vim.fn.stdpath("config"),
  "/extras/JavaVersion.java")

-- if either path is invalid
if not cache_path or not src_path then
  return
end

--- build a cache of the JavaVersion code
local function build_cache()
  -- check if we have javac
  if vim.fn.executable("javac") ~= 1 then
    cache_path = nil
    return
  end

  -- compile our code
  vim.system({ "javac", src_path, "-d", vim.fn.stdpath("cache"), }, {},
    function(out)
      if out.code ~= 0 then
        cache_path = nil
      end
    end)
end

-- check if we have a compiled version of JavaVersion
local f, _ = io.open(cache_path, "r")
if not f then -- if we don"t have a cache
  build_cache()
else
  io.close(f)
end

local function version_check()
  -- check the java version
  local out = vim.system({
    config.cmd[1], -- use the same java executable that the lsp will be using
    (cache_path and "JavaVersion") or src_path
  }, { cwd = vim.fn.stdpath("cache"), text = true }):wait()

  -- parse the version information aquired
  local v = vim.version.parse(out.stdout)

  -- if there's an error, no version info, or the java version is
  -- less than 21 stop the lsp from starting
  if out.code ~= 0 then
    vim.notify(string.format(
      "java version check failed: exit code %s", out.code),
      vim.log.levels.ERROR, { title = core.misc.appid })
    vim.notify(string.format(
      "%s", vim.inspect(out.stdout)),
      vim.log.levels.ERROR, { title = core.misc.appid })
    return false
  elseif not v then
    vim.notify("no java version info found", vim.log.levels.ERROR,
      { title = core.misc.appid })
    return false
  elseif v.major < 21 then
    vim.notify(string.format(
      "java version %s < 21.0.0 Cannot run jdtls, bailing out",
      v[1].."."..v[2].."."..v[3]),
      vim.log.levels.ERROR, { title = core.misc.appid })
    return false
  end

  return true
end


-- start the jdtls with dap!
if not version_check() then
  return
end
jdtls.start_or_attach(config)