[gnome-builder] jedi-language-server: introduce a python language server



commit a7c2e0696759e29ae950bfddf313c7fd0ee25d76
Author: Günther Wagner <info gunibert de>
Date:   Sat Aug 7 21:39:03 2021 +0200

    jedi-language-server: introduce a python language server

 build-aux/flatpak/org.gnome.Builder.json           |   1 +
 .../flatpak/python3-jedi-language-server.json      |  54 ++++++++
 meson_options.txt                                  |   3 +-
 .../jedi-language-server.plugin                    |  16 +++
 .../jedi_language_server_plugin.py                 | 144 +++++++++++++++++++++
 src/plugins/jedi-language-server/meson.build       |  13 ++
 src/plugins/meson.build                            |   2 +
 7 files changed, 232 insertions(+), 1 deletion(-)
---
diff --git a/build-aux/flatpak/org.gnome.Builder.json b/build-aux/flatpak/org.gnome.Builder.json
index 543cbd554..6e79abe12 100644
--- a/build-aux/flatpak/org.gnome.Builder.json
+++ b/build-aux/flatpak/org.gnome.Builder.json
@@ -94,6 +94,7 @@
     ],
     "modules" : [
         "python-deps.json",
+        "python3-jedi-language-server.json",
         {
             "name" : "gperftools",
             "cleanup" : [
diff --git a/build-aux/flatpak/python3-jedi-language-server.json 
b/build-aux/flatpak/python3-jedi-language-server.json
new file mode 100644
index 000000000..98e62f589
--- /dev/null
+++ b/build-aux/flatpak/python3-jedi-language-server.json
@@ -0,0 +1,54 @@
+{
+    "name": "python3-jedi-language-server",
+    "buildsystem": "simple",
+    "build-commands": [
+        "pip3 install --no-index --find-links=\"file://${PWD}\" --prefix=${FLATPAK_DEST} 
jedi-language-server"
+    ],
+    "sources": [
+        {
+            "type": "file",
+            "url": 
"https://files.pythonhosted.org/packages/aa/55/62e2d4934c282a60b4243a950c9dbfa01ae7cac0e8d6c0b5315b87432c81/typing_extensions-3.10.0.0.tar.gz";,
+            "sha256": "50b6f157849174217d0656f99dc82fe932884fb250826c18350e159ec6cdf342"
+        },
+        {
+            "type": "file",
+            "url": 
"https://files.pythonhosted.org/packages/b9/d2/12a808613937a6b98cd50d6467352f01322dc0d8ca9fb5b94441625d6684/pydantic-1.8.2.tar.gz";,
+            "sha256": "26464e57ccaafe72b7ad156fdaa4e9b9ef051f69e175dbbb463283000c05ab7b"
+        },
+        {
+            "type": "file",
+            "url": 
"https://files.pythonhosted.org/packages/2e/35/6c4fff5ab443b57116cb1aad46421fb719bed2825664e8fe77d66d99bcbc/typing_extensions-3.10.0.0-py3-none-any.whl";,
+            "sha256": "779383f6086d90c99ae41cf0ff39aac8a7937a9283ce0a414e5dd782f4c94a84"
+        },
+        {
+            "type": "file",
+            "url": 
"https://files.pythonhosted.org/packages/a0/88/2a1613174e7d05540358b2f19881f369bfe6ba737f0a673177e69eb623df/typeguard-2.12.1-py3-none-any.whl";,
+            "sha256": "cc15ef2704c9909ef9c80e19c62fb8468c01f75aad12f651922acf4dbe822e02"
+        },
+        {
+            "type": "file",
+            "url": 
"https://files.pythonhosted.org/packages/e3/e2/503d039ade05fa2aeab7a1b2b79ad8af32582f42527a154348cd999b7996/pygls-0.11.2-py3-none-any.whl";,
+            "sha256": "5c5b1c8717a39b97462b197f6186c12d2e16455da00631cdaaca91d25736ffcd"
+        },
+        {
+            "type": "file",
+            "url": 
"https://files.pythonhosted.org/packages/a9/c4/d5476373088c120ffed82f34c74b266ccae31a68d665b837354d4d8dc8be/parso-0.8.2-py2.py3-none-any.whl";,
+            "sha256": "a8c4922db71e4fdb90e0d0bc6e50f9b273d3397925e5e60a717e719201778d22"
+        },
+        {
+            "type": "file",
+            "url": 
"https://files.pythonhosted.org/packages/07/33/1463d00e3836e71251be7ae647b537628378453f174222b81485b4826dc3/docstring_to_markdown-0.9-py3-none-any.whl";,
+            "sha256": "488b9f26339651edc7fc9386caeffd3924569072a8b67b727e253f83d5b18c59"
+        },
+        {
+            "type": "file",
+            "url": 
"https://files.pythonhosted.org/packages/f9/36/7aa67ae2663025b49e8426ead0bad983fee1b73f472536e9790655da0277/jedi-0.18.0-py2.py3-none-any.whl";,
+            "sha256": "18456d83f65f400ab0c2d3319e48520420ef43b23a086fdc05dff34132f0fb93"
+        },
+        {
+            "type": "file",
+            "url": 
"https://files.pythonhosted.org/packages/93/b0/a747a5349448e48432b041fa1c9b1b9fb51a3ed418db9d77ed8d89ddaf6f/jedi_language_server-0.34.1-py3-none-any.whl";,
+            "sha256": "8a1b79de1408b28343b424e0697a22fdfe06b0ae5ce273f7f788cc0057014d11"
+        }
+    ]
+}
\ No newline at end of file
diff --git a/meson_options.txt b/meson_options.txt
index 6c246cb9d..9a7fd032c 100644
--- a/meson_options.txt
+++ b/meson_options.txt
@@ -48,7 +48,8 @@ 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')
+option('plugin_jedi', type: 'boolean', value: false)
+option('plugin_jedi_language_server', type: 'boolean')
 option('plugin_jhbuild', type: 'boolean')
 option('plugin_make', type: 'boolean')
 option('plugin_maven', type: 'boolean')
diff --git a/src/plugins/jedi-language-server/jedi-language-server.plugin 
b/src/plugins/jedi-language-server/jedi-language-server.plugin
new file mode 100644
index 000000000..b6a7e7acf
--- /dev/null
+++ b/src/plugins/jedi-language-server/jedi-language-server.plugin
@@ -0,0 +1,16 @@
+[Plugin]
+Builtin=true
+Copyright=Copyright © 2021 Günther Wagner
+Description=Provides LSP integration for Python
+Hidden=true
+Loader=python3
+Module=jedi_language_server_plugin
+Name=Python Language Server Plugin
+X-Builder-ABI=@PACKAGE_ABI@
+X-Completion-Provider-Languages=python,python3
+X-Symbol-Resolver-Languages=python,python3
+X-Diagnostic-Provider-Languages=python,python3
+X-Highlighter-Languages=python,python3
+X-Hover-Provider-Languages=python,python3
+X-Rename-Provider-Languages=python,python3
+X-Formatter-Languages=python,python3
diff --git a/src/plugins/jedi-language-server/jedi_language_server_plugin.py 
b/src/plugins/jedi-language-server/jedi_language_server_plugin.py
new file mode 100644
index 000000000..fddcef0a1
--- /dev/null
+++ b/src/plugins/jedi-language-server/jedi_language_server_plugin.py
@@ -0,0 +1,144 @@
+#!/usr/bin/env python3
+
+# jedi_language_server_plugin.py
+#
+# Copyright 2021 Günther Wagner <info gunibert de>
+#
+# 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/>.
+#
+# SPDX-License-Identifier: GPL-3.0-or-later
+
+import gi
+
+from gi.repository import GLib
+from gi.repository import Gio
+from gi.repository import GObject
+from gi.repository import Ide
+
+DEV_MODE = True
+
+class JediService(Ide.Object):
+    _client = None
+    _has_started = False
+    _supervisor = None
+
+    @classmethod
+    def from_context(klass, context):
+        return context.ensure_child_typed(JediService)
+
+    @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 _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 jedi-language-server from there
+            workdir = self.get_context().ref_workdir()
+            launcher.set_cwd(workdir.get_path())
+
+            launcher.push_argv("jedi-language-server")
+
+            # 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('python')
+        self._client.add_language('python3')
+        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_run_on_host(True)
+        return launcher
+
+    @classmethod
+    def bind_client(klass, provider):
+        context = provider.get_context()
+        self = JediService.from_context(context)
+        self._ensure_started()
+        self.bind_property('client', provider, 'client', GObject.BindingFlags.SYNC_CREATE)
+
+class JediDiagnosticProvider(Ide.LspDiagnosticProvider, Ide.DiagnosticProvider):
+    def do_load(self):
+        JediService.bind_client(self)
+
+class JediCompletionProvider(Ide.LspCompletionProvider, Ide.CompletionProvider):
+    def do_load(self, context):
+        Ide.debug("load completion provider")
+        JediService.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 JediSymbolResolver(Ide.LspSymbolResolver, Ide.SymbolResolver):
+    def do_load(self):
+        JediService.bind_client(self)
+
+class JediHighlighter(Ide.LspHighlighter, Ide.Highlighter):
+    def do_load(self):
+        JediService.bind_client(self)
+
+class JediFormatter(Ide.LspFormatter, Ide.Formatter):
+    def do_load(self):
+        JediService.bind_client(self)
+
+class JediHoverProvider(Ide.LspHoverProvider, Ide.HoverProvider):
+    def do_prepare(self):
+        self.props.category = 'Python'
+        self.props.priority = 200
+        JediService.bind_client(self)
+
+class JediRenameProvider(Ide.LspRenameProvider, Ide.RenameProvider):
+    def do_load(self):
+        JediService.bind_client(self)
+
diff --git a/src/plugins/jedi-language-server/meson.build b/src/plugins/jedi-language-server/meson.build
new file mode 100644
index 000000000..fe273a8dc
--- /dev/null
+++ b/src/plugins/jedi-language-server/meson.build
@@ -0,0 +1,13 @@
+if get_option('plugin_jedi_language_server')
+
+install_data('jedi_language_server_plugin.py', install_dir: plugindir)
+
+configure_file(
+          input: 'jedi-language-server.plugin',
+         output: 'jedi-language-server.plugin',
+  configuration: config_h,
+        install: true,
+    install_dir: plugindir,
+)
+
+endif
diff --git a/src/plugins/meson.build b/src/plugins/meson.build
index 6df310c80..6a8438b04 100644
--- a/src/plugins/meson.build
+++ b/src/plugins/meson.build
@@ -86,6 +86,7 @@ subdir('history')
 subdir('html-completion')
 subdir('html-preview')
 subdir('jedi')
+subdir('jedi-language-server')
 subdir('jhbuild')
 subdir('line-spacing')
 subdir('ls')
@@ -177,6 +178,7 @@ status += [
   'HTML Completion ....... : @0@'.format(get_option('plugin_html_completion')),
   'HTML Preview .......... : @0@'.format(get_option('plugin_html_preview')),
   'Jedi .................. : @0@'.format(get_option('plugin_jedi')),
+  'Jedi Language Server... : @0@'.format(get_option('plugin_jedi_language_server')),
   'JHBuild ............... : @0@'.format(get_option('plugin_jhbuild')),
   'Make .................. : @0@'.format(get_option('plugin_make')),
   'Maven ................. : @0@'.format(get_option('plugin_maven')),


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