[gnome-builder/gvls] GVls: ported to Ide.LspService



commit c706574f00340354adeb4dac61302092ecb4c177
Author: Daniel Espinosa Ortiz <esodan gmail com>
Date:   Mon Jun 27 09:00:53 2022 -0500

    GVls: ported to Ide.LspService
    
    While this removes some magic to use GLsv (the GVls implementation for Vala)
    under Flatpak build environments, this is the first step to clean up the
    plugin, use the latest API and start to re-implement it to ensure it will
    work for Flatpak different SDKs.

 src/plugins/gvls/gvls_plugin.py | 541 +---------------------------------------
 1 file changed, 8 insertions(+), 533 deletions(-)
---
diff --git a/src/plugins/gvls/gvls_plugin.py b/src/plugins/gvls/gvls_plugin.py
index 0a5051cd5..1206e9c5a 100644
--- a/src/plugins/gvls/gvls_plugin.py
+++ b/src/plugins/gvls/gvls_plugin.py
@@ -3,7 +3,7 @@
 # gvls_plugin.py
 #
 # Copyright 2016 Christian Hergert <chergert redhat com>
-# Copyright 2019 Daniel Espinosa <esodan gmail com>
+# Copyright 2019-2022 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
@@ -31,539 +31,14 @@ from gi.repository import Ide
 
 DEV_MODE = True
 
-class GVlsService(Ide.Object):
-    _client = None
-    _has_started = False
-    _supervisor = None
-    _monitor = None
-    autoconfigure = True
-    meson_build_system = True
-    initialized = True
-    default_namespaces = True
-    default_vapi_dirs = True
-    scan_work_space = False
-    add_using_namespaces = True
-    library_vapidir = ""
-    system_vapidir = ""
-    files = []
-    packages = []
-    vala_args = {}
-    options = []
-    vala_api_version = ""
-    build_system = None
-    pipeline = None
-    build_args = None
-    ide_config = None
-    source_file = None
-    build_monitor = None
-    meson_compile_commands = ""
-    meson_targets_intro = ""
+class GVlsService(Ide.LspService):
+    def __init__(self, *args, **kwargs):
+        super().__init__(self, *args, **kwargs)
+        self.set_program('org.gnome.gvls.stdio.Server')
+        self.set_inherit_stderr(DEV_MODE)
 
-    @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')
-
-    @staticmethod
-    def on_destroy(self):
-        """
-        Stops the Vala Language Server upon request to shutdown the
-        GVlsService.
-        """
-        if self._supervisor is not None:
-            Ide.warning('Stopping GVls supervisor')
-            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)
-            Ide.debug('GVls server supervisor has been prepared')
-            self._supervisor.start()
-
-    def list_to_variant(self, l):
-        b = GLib.VariantBuilder(GLib.VariantType.new('av'))
-        for s in l:
-            v = GLib.Variant.new_string (s)
-            b.add_value(GLib.Variant.new_variant(v))
-        return b.end()
-
-    def dict_to_array_variant(self, d):
-        b = GLib.VariantBuilder(GLib.VariantType.new('av'))
-        for k in d.keys():
-            a = d[k]
-            b2 = GLib.VariantBuilder(GLib.VariantType.new('a{sv}'))
-            ndi = GLib.Variant.new_string ('name')
-            vndi = GLib.Variant.new_variant(GLib.Variant.new_string(k))
-            di = GLib.Variant.new_dict_entry(ndi, vndi)
-            vdi = GLib.Variant.new_variant(di)
-            b2.add_value(di)
-            vdi = GLib.Variant.new_string ('value')
-            vvdi = GLib.Variant.new_variant(GLib.Variant.new_string(a))
-            div = GLib.Variant.new_dict_entry(vdi, vvdi)
-            vdi = GLib.Variant.new_variant(div)
-            b2.add_value(div)
-            b.add_value(GLib.Variant.new_variant(b2.end()))
-        return b.end()
-
-    def create_dict_entry_boolean(self, key, val):
-        vk = GLib.Variant.new_string (key)
-        vv = GLib.Variant.new_variant(GLib.Variant.new_boolean(val))
-        return GLib.Variant.new_dict_entry(vk, vv)
-
-    def create_dict_entry_string(self, key, val):
-        vk = GLib.Variant.new_string (key)
-        vv = GLib.Variant.new_variant(GLib.Variant.new_string(val))
-        return GLib.Variant.new_dict_entry(vk, vv)
-
-    def create_configuration_variant(self):
-        try:
-            b = GLib.VariantBuilder(GLib.VariantType.new('a{sv}'))
-            b.add_value(self.create_dict_entry_boolean('initialized', self.initialized))
-            b.add_value(self.create_dict_entry_boolean('defaultNamespaces', self.default_namespaces))
-            b.add_value(self.create_dict_entry_boolean('defaultVapiDirs', self.default_vapi_dirs))
-            b.add_value(self.create_dict_entry_boolean('scanWorkspace', self.scan_work_space))
-            b.add_value(self.create_dict_entry_boolean('addUsingNamespaces', self.add_using_namespaces))
-            b.add_value(self.create_dict_entry_boolean('mesonBuildSystem', self.meson_build_system))
-            b.add_value(self.create_dict_entry_string('libraryVapi', self.library_vapidir))
-            Ide.debug('Library VAPI dir:{0}'.format(self.library_vapidir))
-            b.add_value(self.create_dict_entry_string('systemVapi', self.system_vapidir))
-            b.add_value(self.create_dict_entry_string('mesonCompileCommands', self.meson_compile_commands))
-            b.add_value(self.create_dict_entry_string('mesonTargetsIntro', self.meson_targets_intro))
-            Ide.debug('System VAPI dir:{0}'.format(self.system_vapidir))
-            if (self.library_vapidir == "" or self.system_vapidir == "" or self.meson_compile_commands == 
""):
-                self.autoconfigure = True
-            else:
-                self.autoconfigure = False
-
-            b.add_value(self.create_dict_entry_boolean('autoConfigure', self.autoconfigure))
-            b.add_value(self.create_dict_entry_string('valaApiVersion', self.vala_api_version))
-            ad = GLib.Variant.new_string ('valaArgs')
-            vadi = self.dict_to_array_variant(self.vala_args)
-            adi = GLib.Variant.new_dict_entry(ad, GLib.Variant.new_variant (vadi))
-            b.add_value(adi)
-            od = GLib.Variant.new_string ('options')
-            vodi = self.list_to_variant(self.options)
-            odi = GLib.Variant.new_dict_entry(od, GLib.Variant.new_variant (vodi))
-            b.add_value(odi)
-            pd = GLib.Variant.new_string ('packages')
-            vpdi = self.list_to_variant(self.packages)
-            pdi = GLib.Variant.new_dict_entry(pd, GLib.Variant.new_variant (vpdi))
-            b.add_value(pdi)
-            fd = GLib.Variant.new_string ('files')
-            vfdi = self.list_to_variant(self.files)
-            fdi = GLib.Variant.new_dict_entry(fd, GLib.Variant.new_variant (vfdi))
-            b.add_value(fdi)
-            return GLib.Variant.new_variant (b.end())
-        except Error as e:
-            Ide.debug ('On Load Configuration Error: {}'.format(e.message))
-            return GLib.Variant ('a{sv}', {})
-
-    def _build_config_changed(self, obj, mfile, ofile, event_type):
-        if event_type == Gio.FileMonitorEvent.CHANGED or event_type == Gio.FileMonitorEvent.CREATED:
-            self._parse_build_commands()
-            self._notify_change_configuration()
-        if event_type == Gio.FileMonitorEvent.DELETED:
-            self.build_monitor = None
-
-    def read_meson_targets_intro(self, file):
-        ostream = Gio.MemoryOutputStream.new_resizable()
-        ostream.splice(file.read(), Gio.OutputStreamSpliceFlags.CLOSE_SOURCE, None)
-        ostream.close()
-        b = ostream.steal_as_bytes()
-        self.meson_targets_intro = str(b.get_data(),encoding='utf8')
-
-    def read_meson_compile_commands(self, file):
-        ostream = Gio.MemoryOutputStream.new_resizable()
-        ostream.splice(file.read(), Gio.OutputStreamSpliceFlags.CLOSE_SOURCE, None)
-        ostream.close()
-        b = ostream.steal_as_bytes()
-        self.meson_compile_commands = str(b.get_data(),encoding='utf8')
-
-    def _parse_build_commands(self):
-        try:
-            self.build_args = []
-            ctx = self._client.ref_context()
-            buildmgr = Ide.BuildManager.from_context (ctx)
-            self.pipeline = buildmgr.get_pipeline ()
-            if self.pipeline != None:
-                if self.pipeline.has_configured():
-                    bcdir = Gio.File.new_for_path(self.pipeline.get_builddir())
-                    if self.meson_build_system:
-                        bcf = Gio.File.new_for_uri(bcdir.get_uri()+'/compile_commands.json')
-                        if not bcf.query_exists(None):
-                            return
-                        mtf = Gio.File.new_for_uri(bcdir.get_uri()+'/meson-info/intro-targets.json')
-                        if not mtf.query_exists(None):
-                            self.read_meson_compile_commands(bcf)
-                            if self.build_monitor == None:
-                                self.build_monitor = bcf.monitor(Gio.FileMonitorFlags.NONE, None)
-                                self.build_monitor.connect('changed', self._build_config_changed)
-                            cc = Ide.CompileCommands.new()
-                            cc.load (bcf)
-                            commands = cc.lookup (self.source_file, '')
-                            if commands != None:
-                                self.build_args = commands[0]
-                        else:
-                            self.read_meson_targets_intro(mtf)
-                            if self.build_monitor == None:
-                                self.build_monitor = mtf.monitor(Gio.FileMonitorFlags.NONE, None)
-                                self.build_monitor.connect('changed', self._build_config_changed)
-            self.files = []
-            self.packages = []
-            self.vala_args = {}
-            self.options = []
-            found_package = False
-            found_arg = False
-            arg_name = ''
-            for s in self.build_args:
-                if found_package:
-                    self.packages += [s]
-                    found_package = False
-                    continue
-                if found_arg:
-                    self.vala_args[arg_name] = s
-                    found_arg = False
-                    continue
-                if s == '--pkg' or s == 'pkg':
-                    found_package = True
-                if s.startswith('-') and not (s == '--pkg' or s == 'pkg'):
-                    if s.startswith('--version'):
-                        continue
-                    if s.startswith('--api-version'):
-                        continue
-                    if s == '-C' or s == '--ccode':
-                        self.vala_args[s] = ''
-                        continue
-                    if s == '--use-header':
-                        self.vala_args[s] = ''
-                        continue
-                    if s == '--fast-vapi':
-                        self.vala_args[s] = ''
-                        continue
-                    if s == '--use-fast-vapi':
-                        self.vala_args[s] = ''
-                        continue
-                    if s == '--vapi-comments':
-                        self.vala_args[s] = ''
-                        continue
-                    if s == '--deps':
-                        self.vala_args[s] = ''
-                        continue
-                    if s == '-c' or s == '--compile':
-                        self.vala_args[s] = ''
-                        continue
-                    if s == '-g' or s == '--debug':
-                        self.vala_args[s] = ''
-                        continue
-                    if s == '--enable-mem-profiler':
-                        self.vala_args[s] = ''
-                        continue
-                    if s == '--nostdpkg':
-                        self.vala_args[s] = ''
-                        continue
-                    if s == '--disable-assert':
-                        self.vala_args[s] = ''
-                        continue
-                    if s == '--enable-checking':
-                        self.vala_args[s] = ''
-                        continue
-                    if s == '--enable-deprecated':
-                        self.vala_args[s] = ''
-                        continue
-                    if s == '--hide-internal':
-                        self.vala_args[s] = ''
-                        continue
-                    if s == '--enable-experimental':
-                        self.vala_args[s] = ''
-                        continue
-                    if s == '--disable-warnings':
-                        self.vala_args[s] = ''
-                        continue
-                    if s == '--fatal-warnings':
-                        self.vala_args[s] = ''
-                        continue
-                    if s == '--disable-since-check':
-                        self.vala_args[s] = ''
-                        continue
-                    if s == '--enable-experimental-non-null':
-                        self.vala_args[s] = ''
-                        continue
-                    if s == '--enable-gobject-tracing':
-                        self.vala_args[s] = ''
-                        continue
-                    if s == '--save-temps':
-                        self.vala_args[s] = ''
-                        continue
-                    if s == '-q' or s == '--quiet':
-                        self.vala_args[s] = ''
-                        continue
-                    if s == '-v' or s == '--verbose':
-                        self.vala_args[s] = ''
-                        continue
-                    if s == '--no-color':
-                        self.vala_args[s] = ''
-                        continue
-                    if s == '--enable-version-header':
-                        self.vala_args[s] = ''
-                        continue
-                    if s == '--disable-version-header':
-                        self.vala_args[s] = ''
-                        continue
-                    if s == '--run-args':
-                        self.vala_args[s] = ''
-                        continue
-                    if s == '--abi-stability':
-                        self.vala_args[s] = ''
-                        continue
-                    if '=' in s:
-                        ps = s.split('=')
-                        if len(ps) == 2:
-                            self.vala_args[ps[0]] = ps[1]
-                        continue
-                    found_arg = True
-                    arg_name = s
-                    continue
-        except BaseException as exc:
-            Ide.debug('Parse Build Commands Error: {}'.format(exc.args))
-
-    def _did_change_configuration(self, source_object, result, user_data):
-        try:
-            self._client.send_notification_finish(result)
-        except BaseException as exc:
-            Ide.debug('Change Configuration Notification error: {}'.format(exc.args))
-
-    def _notify_change_configuration(self):
-        try:
-            vconf = self.create_configuration_variant(None)
-            cancellable = self._client.ref_cancellable()
-            b = GLib.VariantBuilder(GLib.VariantType.new('a{sv}'))
-            vk = GLib.Variant.new_string ('settings')
-            vv = GLib.Variant.new_variant(GLib.Variant.new_variant(vconf))
-            de = GLib.Variant.new_dict_entry(vk, vv)
-            b.add_value(de)
-            vnotify = GLib.Variant.new_variant(b.end())
-            self._client.send_notification_async("workspace/didChangeConfiguration", vnotify, cancellable, 
self._did_change_configuration, None)
-        except BaseException as exc:
-            Ide.debug('Notify change configuration error: {}'.format(exc.args))
-
-    def _on_load_configuration(self, data):
-        ctx = self._client.get_context()
-        bufm = Ide.BufferManager.from_context(ctx)
-        for i in range(bufm.get_n_items()):
-            buf = bufm.get_item(i)
-            f = buf.get_file()
-            if f.get_path().endswith('.vala'):
-                self.source_file = f
-                self._parse_build_commands()
-                break
-        return self.create_configuration_variant()
-
-    def _on_pipeline_loaded(self, obj):
-        try:
-            Ide.debug('GVls: Pipeline loaded')
-            self._notify_change_configuration()
-        except BaseException as exc:
-            Ide.debug('On Pipeline Loaded start get build flags error: {}'.format(exc.args))
-
-    def _on_pipeline_diagnostic(self, obj, diagnostic):
-        try:
-            self.source_file = diagnostic.get_file()
-        except BaseException as exc:
-            Ide.debug('On Pipeline diagnostics start get build flags error: {}'.format(exc.args))
-
-    def on_get_vala_data_dir(self, vdp, cancellable, data):
-        try:
-            if self.pipeline == None:
-                return
-            rt = self.pipeline.get_runtime()
-            if rt == None:
-                return
-            vdpio = vdp.get_stdout_pipe()
-            ddpp = Gio.DataInputStream.new(vdpio)
-            ldp = ddpp.read_line()
-            fgdvapi = Gio.File.new_for_path(str(ldp[0],encoding='utf8'))
-            rtfgdvapi = rt.translate_file(fgdvapi)
-            self.system_vapidir = rtfgdvapi.get_uri() + "/vala/vapi"
-        except BaseException as exc:
-            Ide.debug('On get Vala DATA VAPI DIR: {}'.format(str(exc)))
-
-    def on_get_vapidir(self, vpkgp, cancellable, data):
-        try:
-            if self.pipeline == None:
-                return
-            rt = self.pipeline.get_runtime()
-            if rt == None:
-                return
-            vpstdio = vpkgp.get_stdout_pipe()
-            dpp = Gio.DataInputStream.new(vpstdio)
-            lp = dpp.read_line()
-            flvapi = Gio.File.new_for_path(str(lp[0],encoding='utf8'))
-            rtfvapi = rt.translate_file(flvapi)
-            self.library_vapidir = rtfvapi.get_uri()
-            flags = Gio.SubprocessFlags.STDOUT_PIPE
-            launcher = self.pipeline.create_launcher()
-            if launcher == None:
-                return
-            launcher.set_cwd(GLib.get_home_dir())
-            launcher.set_flags(flags)
-            launcher.push_args (['pkg-config','--variable','datadir','libvala-'+self.vala_api_version])
-            vdp = launcher.spawn(None)
-            vdp.wait_async(None, self.on_get_vala_data_dir, None)
-        except BaseException as exc:
-            Ide.debug('On get Vala VAPI DIR: {}'.format(str(exc)))
-
-    def on_get_vala_api_version(self, valacp, cancellable, data):
-        try:
-            vstdio = valacp.get_stdout_pipe()
-            dp = Gio.DataInputStream.new(vstdio)
-            l = dp.read_line()
-            self.vala_api_version = str(l[0],encoding='utf8')
-            if self.pipeline == None:
-                return
-            rt = self.pipeline.get_runtime()
-            if rt == None:
-                return
-            flags = Gio.SubprocessFlags.STDOUT_PIPE
-            launcher = self.pipeline.create_launcher()
-            if launcher == None:
-                return
-            launcher.set_cwd(GLib.get_home_dir())
-            launcher.set_flags(flags)
-            launcher.push_args (['pkg-config','--variable','vapidir','libvala-'+self.vala_api_version])
-            vpkgp = launcher.spawn(None)
-            vpkgp.wait_async(None, self.on_get_vapidir, None)
-        except BaseException as exc:
-            Ide.debug('On get Vala API VERSION Runtime Configuration: {}'.format(str(exc)))
-
-    def _update_config_from_runtime(self):
-        try:
-            if self.pipeline == None:
-                return
-            rt = self.pipeline.get_runtime()
-            if rt == None:
-                return
-            flags = Gio.SubprocessFlags.STDOUT_PIPE
-            launcher = self.pipeline.create_launcher()
-            if launcher == None:
-                return
-            launcher.set_cwd(GLib.get_home_dir())
-            launcher.set_flags(flags)
-            launcher.push_args (['valac','--api-version'])
-            valacp = launcher.spawn(None)
-            valacp.wait_async(None, self.on_get_vala_api_version, None)
-        except BaseException as exc:
-            Ide.debug('On Update Runtime Configuration: {}'.format(str(exc)))
-
-    def on_config_changed_cb(self, data):
-        try:
-            ctx = self._client.ref_context()
-            buildmgr = Ide.BuildManager.from_context (ctx)
-            self.pipeline = buildmgr.get_pipeline ()
-            self.pipeline.connect('diagnostic', self._on_pipeline_diagnostic)
-        except BaseException as exc:
-            Ide.debug('On Config Changed CB: {}'.format(str(exc)))
-
-    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')
-        try:
-            ctx = self._client.ref_context()
-            self.build_system = Ide.BuildSystem.from_context (ctx)
-            if self.build_system.get_id () == 'meson':
-                self.meson_build_system = True
-            else:
-                self.meson_build_system = False
-            buildmgr = Ide.BuildManager.from_context (ctx)
-            self.pipeline = buildmgr.get_pipeline ()
-            self.pipeline.connect('loaded', self._on_pipeline_loaded)
-            self.pipeline.connect('diagnostic', self._on_pipeline_diagnostic)
-            cfgmgr = Ide.ConfigManager.from_context(ctx)
-            cfgmgr.connect('notify::current', self.on_config_changed_cb)
-            self._update_config_from_runtime()
-        except BaseException as exc:
-            Ide.debug('Exception Arguments: {}'.format(exc.args))
-
-    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())
-        return launcher
-
-    @classmethod
-    def bind_client(klass, provider):
-        context = provider.get_context()
-        self = GVlsService.from_context(context)
-        self._ensure_started()
-        self.connect('destroy', GVlsService.on_destroy)
-        self.bind_property('client', provider, 'client', GObject.BindingFlags.SYNC_CREATE)
+    def do_configure_client(self, client):
+        client.add_language('vala')
 
 class GVlsDiagnosticProvider(Ide.LspDiagnosticProvider, Ide.DiagnosticProvider):
     def do_load(self):


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]