Commit ac319c73 authored by Daniel Espinosa Ortiz's avatar Daniel Espinosa Ortiz Committed by Christian Hergert
Browse files

gvls: add Vala language server plugin

Adds gvls (Vala language server) support in a new plugin. The intention
is to remove the vala-pack plugin as part of this going forward.
parent 57c36849
......@@ -45,6 +45,7 @@ option('plugin_gnome_code_assistance', type: 'boolean')
option('plugin_go_langserv', type: 'boolean')
option('plugin_gradle', type: 'boolean')
option('plugin_grep', type: 'boolean')
option('plugin_gvls', type: 'boolean')
option('plugin_html_completion', type: 'boolean')
option('plugin_html_preview', type: 'boolean')
option('plugin_jedi', type: 'boolean')
......@@ -71,7 +72,7 @@ option('plugin_stylelint', type: 'boolean')
option('plugin_sysprof', type: 'boolean')
option('plugin_sysroot', type: 'boolean')
option('plugin_todo', type: 'boolean')
option('plugin_vala', type: 'boolean')
option('plugin_vala', type: 'boolean', value: false)
option('plugin_vagrant', type: 'boolean', value: false)
option('plugin_valgrind', type: 'boolean')
option('plugin_waf', type: 'boolean')
......
# gvls.plugin
[Plugin]
Author=Daniel Espinosa
Name=GNOME Vala Language Server
Description=GNOME Builder plugin for GVLS using LSP
Copyright=Copyright © 2019 Daniel Espinosa
Builtin=true
Loader=python3
Module=gvls_plugin
X-Builder-ABI=@PACKAGE_ABI@
X-Completion-Provider-Languages=vala
X-Highlighter-Languages=vala
X-Symbol-Resolver-Languages=vala
X-Diagnostic-Provider-Languages=vala
#!/usr/bin/env python
# gvls_plugin.py
#
# Copyright 2016 Christian Hergert <chergert@redhat.com>
# Copyright 2019 Daniel Espinosa <esodan@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 Vala Language Server.
It builds off the generic language service components in libide
by bridging them to our supervised Vala Language Server.
"""
import gi
import os
from gi.repository import GLib
from gi.repository import Gio
from gi.repository import GObject
from gi.repository import Ide
from gi.repository import Gdk
from gi.repository import Gtk
from gi.repository import GtkSource
DEV_MODE = True
class GVlsService(Ide.Object):
_client = None
_has_started = False
_supervisor = None
_monitor = None
@classmethod
def from_context(klass, context):
return context.ensure_child_typed(GVlsService)
@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_parent_set(self, parent):
"""
No useful for VLS
"""
if parent is None:
return
context = self.get_context()
workdir = context.ref_workdir()
def do_stop(self):
"""
Stops the Vala Language Server upon request to shutdown the
GVlsService.
"""
if self._client is not None:
print ("Shutting down server")
_client.stop()
_client.destroy()
if self._supervisor is not None:
supervisor, self._supervisor = self._supervisor, None
supervisor.stop()
def _ensure_started(self):
"""
Start the Vala service which provides communication with the
Vala Language Server. We supervise our own instance of the
language server and restart it as necessary using the
Ide.SubprocessSupervisor.
Various extension points (diagnostics, symbol providers, etc) use
the GVlsService to access the rust components they need.
"""
# To avoid starting the `gvls` process unconditionally at startup,
# we 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
# Setup a launcher to spawn the rust language server
launcher = self._create_launcher()
launcher.set_clear_env(False)
# Locate the directory of the project and run gvls from there.
workdir = self.get_context().ref_workdir()
launcher.set_cwd(workdir.get_path())
# If org.gnome.gvls.stdio.Server is installed by GVls
path = 'org.gnome.gvls.stdio.Server'
# Setup our Argv. We want to communicate over STDIN/STDOUT,
# so it does not require any command line options.
launcher.push_argv(path)
# 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._gvls_spawned)
self._supervisor.set_launcher(launcher)
self._supervisor.start()
def _on_load_configuration(self, client):
return GLib.Variant('a{sv}', {
'initialized': GLib.Variant.new_boolean(True),
'defaultNamespaces': GLib.Variant.new_boolean(True),
'defaultVapiDirs': GLib.Variant.new_boolean(True),
'scanWorkspace': GLib.Variant.new_boolean(True),
'addUsingNamespaces': GLib.Variant.new_boolean(True),
'packages': GLib.Variant.new_strv([]),
'options': GLib.Variant.new_strv([]),
})
def _gvls_spawned(self, supervisor, subprocess):
"""
This callback is executed when the `org.gnome.gvls.stdio.Server` process is spawned.
We can use the stdin/stdout to create a channel for our
LspClient.
"""
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._client.connect('load-configuration', self._on_load_configuration)
self.append(self._client)
self._client.add_language('vala')
self._client.start()
self.notify('client')
def _create_launcher(self):
"""
Creates a launcher to be used by the vala service.
In the future, we might be able to rely on the runtime for
the tooling. Maybe even the program if flatpak-builder has
prebuilt our dependencies.
"""
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):
"""
This helper tracks changes to our client as it might happen when
our `org.gnome.gvls.Server` process has crashed.
"""
context = provider.get_context()
self = GVlsService.from_context(context)
self._ensure_started()
self.bind_property('client', provider, 'client', GObject.BindingFlags.SYNC_CREATE)
class GVlsDiagnosticProvider(Ide.LspDiagnosticProvider, Ide.DiagnosticProvider):
def do_load(self):
GVlsService.bind_client(self)
class GVlsCompletionProvider(Ide.LspCompletionProvider, Ide.CompletionProvider):
def do_load(self, context):
GVlsService.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 GVlsHighlighter(Ide.LspHighlighter, Ide.Highlighter):
def do_load(self):
GVlsService.bind_client(self)
class GVlsSymbolResolver(Ide.LspSymbolResolver, Ide.SymbolResolver):
def do_load(self):
GVlsService.bind_client(self)
if get_option('plugin_gvls')
install_data('gvls_plugin.py', install_dir: plugindir)
configure_file(
input: 'gvls.plugin',
output: 'gvls.plugin',
configuration: config_h,
install: true,
install_dir: plugindir,
)
gvls_stdio_server = find_program('org.gnome.gvls.stdio.Server', required: false)
endif
......@@ -125,6 +125,7 @@ subdir('vim')
subdir('waf')
subdir('words')
subdir('xml-pack')
subdir('gvls')
plugins = static_library('plugins', plugins_sources,
dependencies: plugins_deps,
......@@ -186,6 +187,7 @@ status += [
'Sysprof ............... : @0@'.format(get_option('plugin_sysprof')),
'Sysroot ............... : @0@'.format(get_option('plugin_sysroot')),
'Todo .................. : @0@'.format(get_option('plugin_todo')),
'Vala Language Server... : @0@'.format(get_option('plugin_gvls')),
'Vala Pack ............. : @0@'.format(get_option('plugin_vala')),
'Vagrant ............... : @0@'.format(get_option('plugin_vagrant')),
'Valgrind .............. : @0@'.format(get_option('plugin_valgrind')),
......
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