Commit 24985759 authored by Alberto Fanjul's avatar Alberto Fanjul
Browse files

java-langserv: LSP integration for Java

Basic integration with ls running from host
parent 6eec6cc6
Pipeline #80029 passed with stages
in 45 minutes and 57 seconds
......@@ -47,6 +47,7 @@ option('plugin_gradle', type: 'boolean')
option('plugin_grep', type: 'boolean')
option('plugin_html_completion', type: 'boolean')
option('plugin_html_preview', type: 'boolean')
option('plugin_java_langserv', type: 'boolean')
option('plugin_jedi', type: 'boolean')
option('plugin_jhbuild', type: 'boolean')
option('plugin_make', type: 'boolean')
......
# language server client for java
Wrap [java-language-server](https://github.com/georgewfraser/java-language-server).
## Installing and testing
1. Install `java-language-server`
```
git clone https://github.com/georgewfraser/java-language-server
cd java-language-server
JAVA_HOME=/path/to/jdk11 scripts/link_mac.sh
```
2. Set up your environment
Create a launcher in /usr/bin
```
$ cat > /usr/bin/javals <<EOF
#!/bin/bash
/path/to/java-language-server/dist/mac/bin/launcher $@
```
Alternatively create a script in a custom path with `JAVALS_CMD` env:
```
export JAVALS_CMD="/custom/path/to/javals-script"
```
3. Launch Builder, open a java project
4. Right click on a method and click 'go to definition'
## Bugs:
Please report on IRC or gitlab
[Plugin]
Builtin=true
Copyright=Copyright © 2019 Alberto Fanjul
Description=Provides LSP integration for java
Loader=python3
Module=java_langserver_plugin
Name=Java Language Server Plugin
X-Builder-ABI=@PACKAGE_ABI@
X-Symbol-Resolver-Languages=java
X-Completion-Provider-Languages=java
X-Diagnostic-Provider-Languages=java
X-Formatter-Languages=java
X-Highlighter-Languages=java
X-Hover-Provider-Languages=java
X-Rename-Provider-Languages=java
#!/usr/bin/env python3
# java_langserv_plugin.py
#
# Copyright 2019 Alberto Fanjul <albertofanjul@gmail.com>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
"""
This plugin provides integration with the Java Language Server.
It builds off the generic language service components in libide
by bridging them to our supervised Java Language Server.
"""
import os
import gi
from gi.repository import GLib
from gi.repository import Gio
from gi.repository import GObject
from gi.repository import Ide
DEV_MODE = os.getenv('DEV_MODE') and True or False
class JavaService(Ide.Object):
_client = None
_has_started = False
_supervisor = None
@classmethod
def from_context(klass, context):
return context.ensure_child_typed(JavaService)
@GObject.Property(type=Ide.LspClient)
def client(self):
return self._client
@client.setter
def client(self, value):
self._client = value
self.notify('client')
def do_stop(self):
if self._supervisor:
supervisor, self._supervisor = self._supervisor, None
supervisor.stop()
def _which_java_lanserver(self):
path = os.getenv('JAVALS_CMD')
if path and os.path.exists(os.path.expanduser(path)):
return path
return "javals"
def _ensure_started(self):
# To avoid starting the process unconditionally at startup, lazily
# start it when the first provider tries to bind a client to its
# :client property.
if not self._has_started:
self._has_started = True
launcher = self._create_launcher()
launcher.set_clear_env(False)
# Locate the directory of the project and run java-langserver from there
workdir = self.get_context().ref_workdir()
launcher.set_cwd(workdir.get_path())
launcher.push_argv(self._which_java_lanserver())
if DEV_MODE:
launcher.push_argv("--quiet")
# Spawn our peer process and monitor it for
# crashes. We may need to restart it occasionally.
self._supervisor = Ide.SubprocessSupervisor()
self._supervisor.connect('spawned', self._ls_spawned)
self._supervisor.set_launcher(launcher)
self._supervisor.start()
def _ls_spawned(self, supervisor, subprocess):
stdin = subprocess.get_stdin_pipe()
stdout = subprocess.get_stdout_pipe()
io_stream = Gio.SimpleIOStream.new(stdout, stdin)
if self._client:
self._client.stop()
self._client.destroy()
self._client = Ide.LspClient.new(io_stream)
self.append(self._client)
self._client.add_language('java')
self._client.start()
self.notify('client')
def _create_launcher(self):
flags = Gio.SubprocessFlags.STDIN_PIPE | Gio.SubprocessFlags.STDOUT_PIPE
if not DEV_MODE:
flags |= Gio.SubprocessFlags.STDERR_SILENCE
launcher = Ide.SubprocessLauncher()
launcher.set_flags(flags)
launcher.set_cwd(GLib.get_home_dir())
launcher.set_run_on_host(True)
return launcher
@classmethod
def bind_client(klass, provider):
context = provider.get_context()
self = JavaService.from_context(context)
self._ensure_started()
self.bind_property('client', provider, 'client', GObject.BindingFlags.SYNC_CREATE)
class JavaDiagnosticProvider(Ide.LspDiagnosticProvider):
def do_load(self):
JavaService.bind_client(self)
class JavaRenameProvider(Ide.LspRenameProvider):
def do_load(self):
JavaService.bind_client(self)
class JavaSymbolResolver(Ide.LspSymbolResolver, Ide.SymbolResolver):
def do_load(self):
JavaService.bind_client(self)
class JavaHighlighter(Ide.LspHighlighter):
def do_load(self):
JavaService.bind_client(self)
class JavaCompletionProvider(Ide.LspCompletionProvider, Ide.CompletionProvider):
def do_load(self, context):
JavaService.bind_client(self)
def do_get_priority(self, context):
# This provider only activates when it is very likely that we
# want the results. So use high priority (negative is better).
return -1000
class JavaFormatter(Ide.LspFormatter, Ide.Formatter):
def do_load(self):
JavaService.bind_client(self)
class JavaHoverProvider(Ide.LspHoverProvider):
def do_prepare(self):
self.props.category = 'Java'
self.props.priority = 200
JavaService.bind_client(self)
if get_option('plugin_java_langserv')
install_data('java_langserver_plugin.py', install_dir: plugindir)
configure_file(
input: 'java-langserv.plugin',
output: 'java-langserv.plugin',
configuration: config_h,
install: true,
install_dir: plugindir,
)
endif
......@@ -78,6 +78,7 @@ subdir('grep')
subdir('history')
subdir('html-completion')
subdir('html-preview')
subdir('java-langserv')
subdir('jedi')
subdir('jhbuild')
subdir('line-spacing')
......@@ -161,6 +162,7 @@ status += [
'Grep .................. : @0@'.format(get_option('plugin_grep')),
'HTML Completion ....... : @0@'.format(get_option('plugin_html_completion')),
'HTML Preview .......... : @0@'.format(get_option('plugin_html_preview')),
'Java Language Server... : @0@'.format(get_option('plugin_java_langserv')),
'Jedi .................. : @0@'.format(get_option('plugin_jedi')),
'JHBuild ............... : @0@'.format(get_option('plugin_jhbuild')),
'Make .................. : @0@'.format(get_option('plugin_make')),
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment