[gnome-builder] go-langserv: Import an LSP integration for Go



commit a4dc151f20aed0681aca921b0e5348cd06f09c2b
Author: Henry Finucane <h finucane gmail com>
Date:   Sat Jan 6 17:29:24 2018 -0800

    go-langserv: Import an LSP integration for Go
    
    This adds a basic language server client for the go-langserv language
    server. Due to the stability of that language server, only the symbol
    resolver interface is currently implemented.
    
    Fixes #343

 meson_options.txt                               |   1 +
 src/plugins/go-langserv/README.md               |  39 ++++++++
 src/plugins/go-langserv/go-langserv.plugin      |   8 ++
 src/plugins/go-langserv/go_langserver_plugin.py | 122 ++++++++++++++++++++++++
 src/plugins/go-langserv/meson.build             |  13 +++
 src/plugins/meson.build                         |   2 +
 6 files changed, 185 insertions(+)
---
diff --git a/meson_options.txt b/meson_options.txt
index f4a00e222..5d279fe8a 100644
--- a/meson_options.txt
+++ b/meson_options.txt
@@ -42,6 +42,7 @@ option('with_gettext', type: 'boolean')
 option('with_git', type: 'boolean')
 option('with_gjs_symbols', type: 'boolean')
 option('with_gnome_code_assistance', type: 'boolean')
+option('with_go_langserv', type: 'boolean')
 option('with_history', type: 'boolean')
 option('with_html_completion', type: 'boolean')
 option('with_html_preview', type: 'boolean')
diff --git a/src/plugins/go-langserv/README.md b/src/plugins/go-langserv/README.md
new file mode 100644
index 000000000..86dc2fa42
--- /dev/null
+++ b/src/plugins/go-langserv/README.md
@@ -0,0 +1,39 @@
+# a quick langserver plugin for go
+
+Meant to wrap [Sourcegraph's](https://github.com/sourcegraph/go-langserver)
+`go-langserver`. It's a straight copy & paste of the rust-langserver
+implementation, with some sketchy additional `bash` magic to try and better
+support flatpak-packaged Builder.
+
+## Installing and testing
+
+1. Install `go`. Its tooling is stable, 'apt install' or 'yum install' will be fine.
+2. Set up your go environment-
+```
+export GOPATH=$HOME/go
+export PATH="$GOPATH/bin:$PATH"
+go get github.com/sourcegraph/go-langserver
+go install github.com/sourcegraph/go-langserver
+```
+3. Launch Builder, open ~/go/src/github.com/sourcegraph as a project
+4. Right click on a method and click 'go to definition'
+
+## Runtime configuration
+Go has a simple environment convention for managing source code. All user code
+imports come from $GOPATH/src, so to manage separate projects, you can change
+your $GOPATH- think of it as a one-variable equivalent to Python's
+`virtualenv`. Developing inside of $GOPATH/src/$PROJECT is the encouraged way
+to do things, but you don't have to. Newer versions of Go define a default
+'global scrum' $GOPATH in $HOME/go.
+
+The standard library is in $GOROOT/src. If you manually install `go` to a place
+that isn't `/usr/local/go`, you'll need to set this, but normally you don't have
+to worry about it.
+
+## Bugs:
+* I have to disable the rust-langserver extension, or else it seems to take
+  precedence
+* `go-langserver` claims to support formatting, but I don't know how to ask
+  Builder to ask it to do that
+* `go-langserver` has recently merged completion support, but it seems crashy
+  from Builder
diff --git a/src/plugins/go-langserv/go-langserv.plugin b/src/plugins/go-langserv/go-langserv.plugin
new file mode 100644
index 000000000..e41ce2e7a
--- /dev/null
+++ b/src/plugins/go-langserv/go-langserv.plugin
@@ -0,0 +1,8 @@
+[Plugin]
+Module=go_langserver_plugin
+Loader=python3
+Name=Go Language Server Plugin
+Description=Provides LSP integration for Go
+Copyright=Copyright © 2018 Henry Finucane
+Builtin=true
+X-Symbol-Resolver-Languages=go
diff --git a/src/plugins/go-langserv/go_langserver_plugin.py b/src/plugins/go-langserv/go_langserver_plugin.py
new file mode 100644
index 000000000..630b2b59b
--- /dev/null
+++ b/src/plugins/go-langserv/go_langserver_plugin.py
@@ -0,0 +1,122 @@
+#!/usr/bin/env python3
+
+import os
+import json
+import gi
+
+gi.require_version('Ide', '1.0')
+
+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 GoService(Ide.Object, Ide.Service):
+    _client = None
+    _has_started = False
+    _supervisor = None
+
+    @GObject.Property(type=Ide.LangservClient)
+    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_go_lanserver(self):
+        path = os.path.expanduser('~/go/bin/go-langserver')
+        if os.path.exists(path):
+            return path
+        return "go-langserver"
+
+    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 go-langserver from there
+            workdir = self.get_context().get_vcs().get_working_directory()
+            launcher.set_cwd(workdir.get_path())
+
+            # Bash will load the host $PATH and $GOPATH (and optionally $GOROOT) for us.
+            # This does mean there will be a possible .bashrc vs .bash_profile
+            # discrepancy. Possibly there is a better native way to make sure that
+            # builder running in flatpak can run processes in the host context with
+            # the host's $PATH.
+            launcher.push_argv("/bin/bash")
+            launcher.push_argv("--login")
+            launcher.push_argv("-c")
+            launcher.push_argv('exec %s %s' % (
+                self._which_go_lanserver(),
+                "-trace" if DEV_MODE else ""))
+
+            # 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 = Ide.LangservClient.new(self.get_context(), io_stream)
+        self._client.add_language('go')
+        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 = context.get_service_typed(GoService)
+        self._ensure_started()
+        self.bind_property('client', provider, 'client', GObject.BindingFlags.SYNC_CREATE)
+
+# This is the only up-to-date looking list of supported things lsp things:
+# https://github.com/sourcegraph/go-langserver/blob/master/langserver/handler.go#L226
+
+class GoSymbolResolver(Ide.LangservSymbolResolver, Ide.SymbolResolver):
+    def do_load(self):
+        GoService.bind_client(self)
+
+## This is supported as of a few weeks ago, but at least for me, it seems
+## awfully crashy, so I'm going to leave it disabled by default so as to
+## not give a bad impression
+#class GoCompletionProvider(Ide.LangservCompletionProvider, GtkSource.CompletionProvider, 
Ide.CompletionProvider):
+#    def do_load(self, context):
+#        GoService.bind_client(self)
+
+## Could not validate that this works, though `go-langserver` says it does.
+## Calling out to `gofmt` is probably the more canonical route
+#class GoFormatter(Ide.LangservFormatter, Ide.Formatter):
+#    def do_load(self):
+#        GoService.bind_client(self)
diff --git a/src/plugins/go-langserv/meson.build b/src/plugins/go-langserv/meson.build
new file mode 100644
index 000000000..2e665ba3b
--- /dev/null
+++ b/src/plugins/go-langserv/meson.build
@@ -0,0 +1,13 @@
+if get_option('with_go_langserv')
+
+install_data('go_langserver_plugin.py', install_dir: plugindir)
+
+configure_file(
+          input: 'go-langserv.plugin',
+         output: 'go-langserv.plugin',
+  configuration: configuration_data(),
+        install: true,
+    install_dir: plugindir,
+)
+
+endif
diff --git a/src/plugins/meson.build b/src/plugins/meson.build
index 2c63078fa..b0ae89ff4 100644
--- a/src/plugins/meson.build
+++ b/src/plugins/meson.build
@@ -35,6 +35,7 @@ subdir('gettext')
 subdir('git')
 subdir('gjs-symbols')
 subdir('gnome-code-assistance')
+subdir('go-langserv')
 subdir('history')
 subdir('html-completion')
 subdir('html-preview')
@@ -113,6 +114,7 @@ status += [
   'Git ................... : @0@'.format(get_option('with_git')),
   'GJS Symbol Resolver ... : @0@'.format(get_option('with_gjs_symbols')),
   'GNOME Code Assistance . : @0@'.format(get_option('with_gnome_code_assistance')),
+  'Go Language Server .... : @0@'.format(get_option('with_go_langserv')),
   'History ............... : @0@'.format(get_option('with_history')),
   'HTML Completion ....... : @0@'.format(get_option('with_html_completion')),
   'HTML Preview .......... : @0@'.format(get_option('with_html_preview')),


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