[gnome-builder/wip/chergert/lsp-plugin-loader: 6/24] libide/lsp: implement generic LSP server support




commit e5ba93c9b1fd42d85cbc22c3aaf2d31ffeebb478
Author: Christian Hergert <chergert redhat com>
Date:   Tue Oct 11 10:34:21 2022 -0500

    libide/lsp: implement generic LSP server support
    
    This provides dynamic type registration for language server plugins so
    that it does not require writing code in the plugin (unless we end up
    supporting a very odd Language Server).
    
    This will very likely expose some issues in our IdeLspClient that we
    need to figure out, so I expect a bit of fallout as a side-effect.
    
    Additionally, this is going to require that we abstract LSP settings
    using something like `.vscode/settings.json`. Currently, our format
    differs a bit from that (and still needs some implementation) but this
    moves us in the right direction.
    
    The next steps are going to be making that format more familiar, along
    with making it loaded as part of LSP configuration. Beyond that, we need
    to make more generic plumbing work as expected by the LSP peers;
    especially when dealing with capabilities.
    
    Additional fallout from this will be the temporary loss of some
    configuration of LSPs like rust-analyzer which we had tweais.ui for. We
    can probably introduce something like that using tweaks.ui and
    auto-load/bind those to known settings options in settings.json.
    
    Bundled language servers will also be deleted with this patch series as
    we don't want to be doing that anymore anyway. We expect that build
    containers contain the SDK tooling.

 .../lsp/ide-lsp-plugin-code-action-provider.c      | 100 ++++++
 .../lsp/ide-lsp-plugin-completion-provider.c       |  89 ++++++
 .../lsp/ide-lsp-plugin-diagnostic-provider.c       | 100 ++++++
 src/libide/lsp/ide-lsp-plugin-formatter.c          | 100 ++++++
 src/libide/lsp/ide-lsp-plugin-highlighter.c        | 100 ++++++
 src/libide/lsp/ide-lsp-plugin-hover-provider.c     |  88 +++++
 src/libide/lsp/ide-lsp-plugin-private.h            |  78 +++++
 src/libide/lsp/ide-lsp-plugin-rename-provider.c    | 100 ++++++
 src/libide/lsp/ide-lsp-plugin-symbol-resolver.c    | 100 ++++++
 src/libide/lsp/ide-lsp-plugin.c                    | 354 +++++++++++++++++++++
 src/libide/lsp/ide-lsp-plugin.h                    |  32 ++
 src/libide/lsp/libide-lsp.h                        |   1 +
 src/libide/lsp/meson.build                         |  11 +
 13 files changed, 1253 insertions(+)
---
diff --git a/src/libide/lsp/ide-lsp-plugin-code-action-provider.c 
b/src/libide/lsp/ide-lsp-plugin-code-action-provider.c
new file mode 100644
index 000000000..672955cd6
--- /dev/null
+++ b/src/libide/lsp/ide-lsp-plugin-code-action-provider.c
@@ -0,0 +1,100 @@
+/* ide-lsp-plugin-code-action-provider.c
+ *
+ * Copyright 2022 Christian Hergert <chergert redhat 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/>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-or-later
+ */
+
+#define G_LOG_DOMAIN "ide-lsp-plugin-code-action-provider"
+
+#include "config.h"
+
+#include <libpeas/peas.h>
+
+#include <libide-code.h>
+
+#include "ide-lsp-code-action-provider.h"
+#include "ide-lsp-plugin-private.h"
+#include "ide-lsp-service.h"
+
+typedef struct _IdeLspPluginCodeActionProviderClass
+{
+  IdeLspCodeActionProviderClass  parent_class;
+  IdeLspPluginInfo              *info;
+} IdeLspPluginCodeActionProviderClass;
+
+static void
+ide_lsp_plugin_code_action_provider_load (IdeCodeActionProvider *provider)
+{
+  IdeLspPluginCodeActionProviderClass *klass = (IdeLspPluginCodeActionProviderClass *)(((GTypeInstance 
*)provider)->g_class);
+  g_autoptr(IdeLspServiceClass) service_class = g_type_class_ref (klass->info->service_type);
+
+  ide_lsp_service_class_bind_client (service_class, IDE_OBJECT (provider));
+}
+
+static void
+ide_lsp_plugin_code_action_provider_class_init (IdeLspPluginCodeActionProviderClass *klass,
+                                                IdeLspPluginInfo                    *info)
+{
+  klass->info = info;
+}
+
+static void
+ide_lsp_plugin_code_action_provider_iface_init (IdeCodeActionProviderInterface *iface)
+{
+  iface->load = ide_lsp_plugin_code_action_provider_load;
+}
+
+G_GNUC_BEGIN_IGNORE_DEPRECATIONS
+GObject *
+ide_lsp_plugin_create_code_action_provider (guint             n_parameters,
+                                            GParameter       *parameters,
+                                            IdeLspPluginInfo *info)
+{
+  ide_lsp_plugin_remove_plugin_info_param (&n_parameters, parameters);
+
+  if G_UNLIKELY (info->code_action_provider_type == G_TYPE_INVALID)
+    {
+      g_autofree char *name = g_strconcat (info->module_name, "+CodeActionProvider", NULL);
+
+      info->code_action_provider_type =
+        g_type_register_static (IDE_TYPE_LSP_CODE_ACTION_PROVIDER,
+                                name,
+                                &(GTypeInfo) {
+                                  sizeof (IdeLspPluginCodeActionProviderClass),
+                                  NULL,
+                                  NULL,
+                                  (GClassInitFunc)ide_lsp_plugin_code_action_provider_class_init,
+                                  NULL,
+                                  ide_lsp_plugin_info_ref (info),
+                                  sizeof (IdeLspCodeActionProvider),
+                                  0,
+                                  NULL,
+                                  NULL,
+                                },
+                                G_TYPE_FLAG_FINAL);
+      g_type_add_interface_static (info->code_action_provider_type,
+                                   IDE_TYPE_CODE_ACTION_PROVIDER,
+                                   &(GInterfaceInfo) {
+                                     (GInterfaceInitFunc)(void (*) 
(void))ide_lsp_plugin_code_action_provider_iface_init,
+                                     NULL,
+                                     NULL,
+                                   });
+    }
+
+  return g_object_newv (info->code_action_provider_type, n_parameters, parameters);
+}
+G_GNUC_END_IGNORE_DEPRECATIONS
diff --git a/src/libide/lsp/ide-lsp-plugin-completion-provider.c 
b/src/libide/lsp/ide-lsp-plugin-completion-provider.c
new file mode 100644
index 000000000..2db770fcc
--- /dev/null
+++ b/src/libide/lsp/ide-lsp-plugin-completion-provider.c
@@ -0,0 +1,89 @@
+/* ide-lsp-plugin-completion-provider.c
+ *
+ * Copyright 2022 Christian Hergert <chergert redhat 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/>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-or-later
+ */
+
+#define G_LOG_DOMAIN "ide-lsp-plugin-completion-provider"
+
+#include "config.h"
+
+#include <libpeas/peas.h>
+
+#include "ide-lsp-completion-provider.h"
+#include "ide-lsp-plugin-private.h"
+#include "ide-lsp-service.h"
+
+typedef struct _IdeLspPluginCompletionProviderClass
+{
+  IdeLspCompletionProviderClass  parent_class;
+  IdeLspPluginInfo              *info;
+} IdeLspPluginCompletionProviderClass;
+
+static void
+ide_lsp_plugin_completion_provider_load (IdeLspCompletionProvider *provider)
+{
+  IdeLspPluginCompletionProviderClass *klass = (IdeLspPluginCompletionProviderClass *)(((GTypeInstance 
*)provider)->g_class);
+  g_autoptr(IdeLspServiceClass) service_class = g_type_class_ref (klass->info->service_type);
+
+  ide_lsp_service_class_bind_client (service_class, IDE_OBJECT (provider));
+}
+
+static void
+ide_lsp_plugin_completion_provider_class_init (IdeLspPluginCompletionProviderClass *klass,
+                                               IdeLspPluginInfo                    *info)
+{
+  IdeLspCompletionProviderClass *completion_class = IDE_LSP_COMPLETION_PROVIDER_CLASS (klass);
+
+  completion_class->load = ide_lsp_plugin_completion_provider_load;
+
+  klass->info = info;
+}
+
+G_GNUC_BEGIN_IGNORE_DEPRECATIONS
+GObject *
+ide_lsp_plugin_create_completion_provider (guint             n_parameters,
+                                           GParameter       *parameters,
+                                           IdeLspPluginInfo *info)
+{
+  ide_lsp_plugin_remove_plugin_info_param (&n_parameters, parameters);
+
+  if G_UNLIKELY (info->completion_provider_type == G_TYPE_INVALID)
+    {
+      g_autofree char *name = g_strconcat (info->module_name, "+CompletionProvider", NULL);
+
+      info->completion_provider_type =
+        g_type_register_static (IDE_TYPE_LSP_COMPLETION_PROVIDER,
+                                name,
+                                &(GTypeInfo) {
+                                  sizeof (IdeLspPluginCompletionProviderClass),
+                                  NULL,
+                                  NULL,
+                                  (GClassInitFunc)ide_lsp_plugin_completion_provider_class_init,
+                                  NULL,
+                                  ide_lsp_plugin_info_ref (info),
+                                  sizeof (IdeLspCompletionProvider),
+                                  0,
+                                  NULL,
+                                  NULL,
+                                },
+                                G_TYPE_FLAG_FINAL);
+    }
+
+  return g_object_newv (info->completion_provider_type, n_parameters, parameters);
+}
+G_GNUC_END_IGNORE_DEPRECATIONS
diff --git a/src/libide/lsp/ide-lsp-plugin-diagnostic-provider.c 
b/src/libide/lsp/ide-lsp-plugin-diagnostic-provider.c
new file mode 100644
index 000000000..3eade89ad
--- /dev/null
+++ b/src/libide/lsp/ide-lsp-plugin-diagnostic-provider.c
@@ -0,0 +1,100 @@
+/* ide-lsp-plugin-diagnostic-provider.c
+ *
+ * Copyright 2022 Christian Hergert <chergert redhat 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/>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-or-later
+ */
+
+#define G_LOG_DOMAIN "ide-lsp-plugin-diagnostic-provider"
+
+#include "config.h"
+
+#include <libpeas/peas.h>
+
+#include <libide-code.h>
+
+#include "ide-lsp-diagnostic-provider.h"
+#include "ide-lsp-plugin-private.h"
+#include "ide-lsp-service.h"
+
+typedef struct _IdeLspPluginDiagnosticProviderClass
+{
+  IdeLspDiagnosticProviderClass  parent_class;
+  IdeLspPluginInfo              *info;
+} IdeLspPluginDiagnosticProviderClass;
+
+static void
+ide_lsp_plugin_diagnostic_provider_load (IdeDiagnosticProvider *provider)
+{
+  IdeLspPluginDiagnosticProviderClass *klass = (IdeLspPluginDiagnosticProviderClass *)(((GTypeInstance 
*)provider)->g_class);
+  g_autoptr(IdeLspServiceClass) service_class = g_type_class_ref (klass->info->service_type);
+
+  ide_lsp_service_class_bind_client (service_class, IDE_OBJECT (provider));
+}
+
+static void
+ide_lsp_plugin_diagnostic_provider_class_init (IdeLspPluginDiagnosticProviderClass *klass,
+                                               IdeLspPluginInfo                    *info)
+{
+  klass->info = info;
+}
+
+static void
+ide_lsp_plugin_diagnostic_provider_iface_init (IdeDiagnosticProviderInterface *iface)
+{
+  iface->load = ide_lsp_plugin_diagnostic_provider_load;
+}
+
+G_GNUC_BEGIN_IGNORE_DEPRECATIONS
+GObject *
+ide_lsp_plugin_create_diagnostic_provider (guint             n_parameters,
+                                           GParameter       *parameters,
+                                           IdeLspPluginInfo *info)
+{
+  ide_lsp_plugin_remove_plugin_info_param (&n_parameters, parameters);
+
+  if G_UNLIKELY (info->diagnostic_provider_type == G_TYPE_INVALID)
+    {
+      g_autofree char *name = g_strconcat (info->module_name, "+DiagnosticProvider", NULL);
+
+      info->diagnostic_provider_type =
+        g_type_register_static (IDE_TYPE_LSP_DIAGNOSTIC_PROVIDER,
+                                name,
+                                &(GTypeInfo) {
+                                  sizeof (IdeLspPluginDiagnosticProviderClass),
+                                  NULL,
+                                  NULL,
+                                  (GClassInitFunc)ide_lsp_plugin_diagnostic_provider_class_init,
+                                  NULL,
+                                  ide_lsp_plugin_info_ref (info),
+                                  sizeof (IdeLspDiagnosticProvider),
+                                  0,
+                                  NULL,
+                                  NULL,
+                                },
+                                G_TYPE_FLAG_FINAL);
+      g_type_add_interface_static (info->diagnostic_provider_type,
+                                   IDE_TYPE_DIAGNOSTIC_PROVIDER,
+                                   &(GInterfaceInfo) {
+                                     (GInterfaceInitFunc)(void (*) 
(void))ide_lsp_plugin_diagnostic_provider_iface_init,
+                                     NULL,
+                                     NULL,
+                                   });
+    }
+
+  return g_object_newv (info->diagnostic_provider_type, n_parameters, parameters);
+}
+G_GNUC_END_IGNORE_DEPRECATIONS
diff --git a/src/libide/lsp/ide-lsp-plugin-formatter.c b/src/libide/lsp/ide-lsp-plugin-formatter.c
new file mode 100644
index 000000000..5ec5d990b
--- /dev/null
+++ b/src/libide/lsp/ide-lsp-plugin-formatter.c
@@ -0,0 +1,100 @@
+/* ide-lsp-plugin-formatter.c
+ *
+ * Copyright 2022 Christian Hergert <chergert redhat 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/>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-or-later
+ */
+
+#define G_LOG_DOMAIN "ide-lsp-plugin-formatter"
+
+#include "config.h"
+
+#include <libpeas/peas.h>
+
+#include <libide-code.h>
+
+#include "ide-lsp-formatter.h"
+#include "ide-lsp-plugin-private.h"
+#include "ide-lsp-service.h"
+
+typedef struct _IdeLspPluginFormatterClass
+{
+  IdeLspFormatterClass  parent_class;
+  IdeLspPluginInfo     *info;
+} IdeLspPluginFormatterClass;
+
+static void
+ide_lsp_plugin_formatter_load (IdeFormatter *resolver)
+{
+  IdeLspPluginFormatterClass *klass = (IdeLspPluginFormatterClass *)(((GTypeInstance *)resolver)->g_class);
+  g_autoptr(IdeLspServiceClass) service_class = g_type_class_ref (klass->info->service_type);
+
+  ide_lsp_service_class_bind_client (service_class, IDE_OBJECT (resolver));
+}
+
+static void
+ide_lsp_plugin_formatter_class_init (IdeLspPluginFormatterClass *klass,
+                                     IdeLspPluginInfo           *info)
+{
+  klass->info = info;
+}
+
+static void
+ide_lsp_plugin_formatter_iface_init (IdeFormatterInterface *iface)
+{
+  iface->load = ide_lsp_plugin_formatter_load;
+}
+
+G_GNUC_BEGIN_IGNORE_DEPRECATIONS
+GObject *
+ide_lsp_plugin_create_formatter (guint             n_parameters,
+                                       GParameter       *parameters,
+                                       IdeLspPluginInfo *info)
+{
+  ide_lsp_plugin_remove_plugin_info_param (&n_parameters, parameters);
+
+  if G_UNLIKELY (info->formatter_type == G_TYPE_INVALID)
+    {
+      g_autofree char *name = g_strconcat (info->module_name, "+Formatter", NULL);
+
+      info->formatter_type =
+        g_type_register_static (IDE_TYPE_LSP_FORMATTER,
+                                name,
+                                &(GTypeInfo) {
+                                  sizeof (IdeLspPluginFormatterClass),
+                                  NULL,
+                                  NULL,
+                                  (GClassInitFunc)ide_lsp_plugin_formatter_class_init,
+                                  NULL,
+                                  ide_lsp_plugin_info_ref (info),
+                                  sizeof (IdeLspFormatter),
+                                  0,
+                                  NULL,
+                                  NULL,
+                                },
+                                G_TYPE_FLAG_FINAL);
+      g_type_add_interface_static (info->formatter_type,
+                                   IDE_TYPE_FORMATTER,
+                                   &(GInterfaceInfo) {
+                                     (GInterfaceInitFunc)(void (*) 
(void))ide_lsp_plugin_formatter_iface_init,
+                                     NULL,
+                                     NULL,
+                                   });
+    }
+
+  return g_object_newv (info->formatter_type, n_parameters, parameters);
+}
+G_GNUC_END_IGNORE_DEPRECATIONS
diff --git a/src/libide/lsp/ide-lsp-plugin-highlighter.c b/src/libide/lsp/ide-lsp-plugin-highlighter.c
new file mode 100644
index 000000000..bb1d577db
--- /dev/null
+++ b/src/libide/lsp/ide-lsp-plugin-highlighter.c
@@ -0,0 +1,100 @@
+/* ide-lsp-plugin-highlighter.c
+ *
+ * Copyright 2022 Christian Hergert <chergert redhat 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/>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-or-later
+ */
+
+#define G_LOG_DOMAIN "ide-lsp-plugin-highlighter"
+
+#include "config.h"
+
+#include <libpeas/peas.h>
+
+#include <libide-code.h>
+
+#include "ide-lsp-highlighter.h"
+#include "ide-lsp-plugin-private.h"
+#include "ide-lsp-service.h"
+
+typedef struct _IdeLspPluginHighlighterClass
+{
+  IdeLspHighlighterClass  parent_class;
+  IdeLspPluginInfo       *info;
+} IdeLspPluginHighlighterClass;
+
+static void
+ide_lsp_plugin_highlighter_load (IdeHighlighter *resolver)
+{
+  IdeLspPluginHighlighterClass *klass = (IdeLspPluginHighlighterClass *)(((GTypeInstance 
*)resolver)->g_class);
+  g_autoptr(IdeLspServiceClass) service_class = g_type_class_ref (klass->info->service_type);
+
+  ide_lsp_service_class_bind_client (service_class, IDE_OBJECT (resolver));
+}
+
+static void
+ide_lsp_plugin_highlighter_class_init (IdeLspPluginHighlighterClass *klass,
+                                       IdeLspPluginInfo             *info)
+{
+  klass->info = info;
+}
+
+static void
+ide_lsp_plugin_highlighter_iface_init (IdeHighlighterInterface *iface)
+{
+  iface->load = ide_lsp_plugin_highlighter_load;
+}
+
+G_GNUC_BEGIN_IGNORE_DEPRECATIONS
+GObject *
+ide_lsp_plugin_create_highlighter (guint             n_parameters,
+                                   GParameter       *parameters,
+                                   IdeLspPluginInfo *info)
+{
+  ide_lsp_plugin_remove_plugin_info_param (&n_parameters, parameters);
+
+  if G_UNLIKELY (info->highlighter_type == G_TYPE_INVALID)
+    {
+      g_autofree char *name = g_strconcat (info->module_name, "+Highlighter", NULL);
+
+      info->highlighter_type =
+        g_type_register_static (IDE_TYPE_LSP_HIGHLIGHTER,
+                                name,
+                                &(GTypeInfo) {
+                                  sizeof (IdeLspPluginHighlighterClass),
+                                  NULL,
+                                  NULL,
+                                  (GClassInitFunc)ide_lsp_plugin_highlighter_class_init,
+                                  NULL,
+                                  ide_lsp_plugin_info_ref (info),
+                                  sizeof (IdeLspHighlighter),
+                                  0,
+                                  NULL,
+                                  NULL,
+                                },
+                                G_TYPE_FLAG_FINAL);
+      g_type_add_interface_static (info->highlighter_type,
+                                   IDE_TYPE_HIGHLIGHTER,
+                                   &(GInterfaceInfo) {
+                                     (GInterfaceInitFunc)(void (*) 
(void))ide_lsp_plugin_highlighter_iface_init,
+                                     NULL,
+                                     NULL,
+                                   });
+    }
+
+  return g_object_newv (info->highlighter_type, n_parameters, parameters);
+}
+G_GNUC_END_IGNORE_DEPRECATIONS
diff --git a/src/libide/lsp/ide-lsp-plugin-hover-provider.c b/src/libide/lsp/ide-lsp-plugin-hover-provider.c
new file mode 100644
index 000000000..ba0fec227
--- /dev/null
+++ b/src/libide/lsp/ide-lsp-plugin-hover-provider.c
@@ -0,0 +1,88 @@
+/* ide-lsp-plugin-hover-provider.c
+ *
+ * Copyright 2022 Christian Hergert <chergert redhat 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/>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-or-later
+ */
+
+#define G_LOG_DOMAIN "ide-lsp-plugin-hover-provider"
+
+#include "config.h"
+
+#include <libpeas/peas.h>
+
+#include <libide-code.h>
+
+#include "ide-lsp-hover-provider.h"
+#include "ide-lsp-plugin-private.h"
+#include "ide-lsp-service.h"
+
+typedef struct _IdeLspPluginHoverProviderClass
+{
+  IdeLspHoverProviderClass  parent_class;
+  IdeLspPluginInfo              *info;
+} IdeLspPluginHoverProviderClass;
+
+static void
+ide_lsp_plugin_hover_provider_prepare (IdeLspHoverProvider *provider)
+{
+  IdeLspPluginHoverProviderClass *klass = (IdeLspPluginHoverProviderClass *)(((GTypeInstance 
*)provider)->g_class);
+  g_autoptr(IdeLspServiceClass) service_class = g_type_class_ref (klass->info->service_type);
+
+  ide_lsp_service_class_bind_client (service_class, IDE_OBJECT (provider));
+}
+
+static void
+ide_lsp_plugin_hover_provider_class_init (IdeLspPluginHoverProviderClass *klass,
+                                          IdeLspPluginInfo               *info)
+{
+  IDE_LSP_HOVER_PROVIDER_CLASS (klass)->prepare = ide_lsp_plugin_hover_provider_prepare;
+  klass->info = info;
+}
+
+G_GNUC_BEGIN_IGNORE_DEPRECATIONS
+GObject *
+ide_lsp_plugin_create_hover_provider (guint             n_parameters,
+                                       GParameter       *parameters,
+                                       IdeLspPluginInfo *info)
+{
+  ide_lsp_plugin_remove_plugin_info_param (&n_parameters, parameters);
+
+  if G_UNLIKELY (info->hover_provider_type == G_TYPE_INVALID)
+    {
+      g_autofree char *name = g_strconcat (info->module_name, "+HoverProvider", NULL);
+
+      info->hover_provider_type =
+        g_type_register_static (IDE_TYPE_LSP_HOVER_PROVIDER,
+                                name,
+                                &(GTypeInfo) {
+                                  sizeof (IdeLspPluginHoverProviderClass),
+                                  NULL,
+                                  NULL,
+                                  (GClassInitFunc)ide_lsp_plugin_hover_provider_class_init,
+                                  NULL,
+                                  ide_lsp_plugin_info_ref (info),
+                                  sizeof (IdeLspHoverProvider),
+                                  0,
+                                  NULL,
+                                  NULL,
+                                },
+                                G_TYPE_FLAG_FINAL);
+    }
+
+  return g_object_newv (info->hover_provider_type, n_parameters, parameters);
+}
+G_GNUC_END_IGNORE_DEPRECATIONS
diff --git a/src/libide/lsp/ide-lsp-plugin-private.h b/src/libide/lsp/ide-lsp-plugin-private.h
new file mode 100644
index 000000000..a25ee4522
--- /dev/null
+++ b/src/libide/lsp/ide-lsp-plugin-private.h
@@ -0,0 +1,78 @@
+/* ide-lsp-plugin-private.h
+ *
+ * Copyright 2022 Christian Hergert <chergert redhat 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/>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-or-later
+ */
+
+#pragma once
+
+#include <gio/gio.h>
+
+#include "ide-lsp-plugin.h"
+
+G_BEGIN_DECLS
+
+typedef struct _IdeLspPluginInfo
+{
+  char *module_name;
+  char **command;
+  char **languages;
+  GBytes *default_settings;
+  GType service_type;
+  GType completion_provider_type;
+  GType code_action_provider_type;
+  GType diagnostic_provider_type;
+  GType formatter_type;
+  GType highlighter_type;
+  GType hover_provider_type;
+  GType rename_provider_type;
+  GType symbol_resolver_type;
+} IdeLspPluginInfo;
+
+IdeLspPluginInfo *ide_lsp_plugin_info_ref                   (IdeLspPluginInfo *info);
+void              ide_lsp_plugin_info_unref                 (IdeLspPluginInfo *info);
+
+G_GNUC_BEGIN_IGNORE_DEPRECATIONS
+void              ide_lsp_plugin_remove_plugin_info_param    (guint            *n_parameters,
+                                                              GParameter       *parameters);
+GObject          *ide_lsp_plugin_create_code_action_provider (guint             n_parameters,
+                                                              GParameter       *parameters,
+                                                              IdeLspPluginInfo *info);
+GObject          *ide_lsp_plugin_create_completion_provider  (guint             n_parameters,
+                                                              GParameter       *parameters,
+                                                              IdeLspPluginInfo *info);
+GObject          *ide_lsp_plugin_create_diagnostic_provider  (guint             n_parameters,
+                                                              GParameter       *parameters,
+                                                              IdeLspPluginInfo *info);
+GObject          *ide_lsp_plugin_create_formatter            (guint             n_parameters,
+                                                              GParameter       *parameters,
+                                                              IdeLspPluginInfo *info);
+GObject          *ide_lsp_plugin_create_highlighter          (guint             n_parameters,
+                                                              GParameter       *parameters,
+                                                              IdeLspPluginInfo *info);
+GObject          *ide_lsp_plugin_create_hover_provider       (guint             n_parameters,
+                                                              GParameter       *parameters,
+                                                              IdeLspPluginInfo *info);
+GObject          *ide_lsp_plugin_create_rename_provider      (guint             n_parameters,
+                                                              GParameter       *parameters,
+                                                              IdeLspPluginInfo *info);
+GObject          *ide_lsp_plugin_create_symbol_resolver      (guint             n_parameters,
+                                                              GParameter       *parameters,
+                                                              IdeLspPluginInfo *info);
+G_GNUC_END_IGNORE_DEPRECATIONS
+
+G_END_DECLS
diff --git a/src/libide/lsp/ide-lsp-plugin-rename-provider.c b/src/libide/lsp/ide-lsp-plugin-rename-provider.c
new file mode 100644
index 000000000..c436db506
--- /dev/null
+++ b/src/libide/lsp/ide-lsp-plugin-rename-provider.c
@@ -0,0 +1,100 @@
+/* ide-lsp-plugin-rename-provider.c
+ *
+ * Copyright 2022 Christian Hergert <chergert redhat 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/>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-or-later
+ */
+
+#define G_LOG_DOMAIN "ide-lsp-plugin-rename-provider"
+
+#include "config.h"
+
+#include <libpeas/peas.h>
+
+#include <libide-code.h>
+
+#include "ide-lsp-rename-provider.h"
+#include "ide-lsp-plugin-private.h"
+#include "ide-lsp-service.h"
+
+typedef struct _IdeLspPluginRenameProviderClass
+{
+  IdeLspRenameProviderClass  parent_class;
+  IdeLspPluginInfo              *info;
+} IdeLspPluginRenameProviderClass;
+
+static void
+ide_lsp_plugin_rename_provider_load (IdeRenameProvider *provider)
+{
+  IdeLspPluginRenameProviderClass *klass = (IdeLspPluginRenameProviderClass *)(((GTypeInstance 
*)provider)->g_class);
+  g_autoptr(IdeLspServiceClass) service_class = g_type_class_ref (klass->info->service_type);
+
+  ide_lsp_service_class_bind_client (service_class, IDE_OBJECT (provider));
+}
+
+static void
+ide_lsp_plugin_rename_provider_class_init (IdeLspPluginRenameProviderClass *klass,
+                                           IdeLspPluginInfo                *info)
+{
+  klass->info = info;
+}
+
+static void
+ide_lsp_plugin_rename_provider_iface_init (IdeRenameProviderInterface *iface)
+{
+  iface->load = ide_lsp_plugin_rename_provider_load;
+}
+
+G_GNUC_BEGIN_IGNORE_DEPRECATIONS
+GObject *
+ide_lsp_plugin_create_rename_provider (guint             n_parameters,
+                                       GParameter       *parameters,
+                                       IdeLspPluginInfo *info)
+{
+  ide_lsp_plugin_remove_plugin_info_param (&n_parameters, parameters);
+
+  if G_UNLIKELY (info->rename_provider_type == G_TYPE_INVALID)
+    {
+      g_autofree char *name = g_strconcat (info->module_name, "+RenameProvider", NULL);
+
+      info->rename_provider_type =
+        g_type_register_static (IDE_TYPE_LSP_RENAME_PROVIDER,
+                                name,
+                                &(GTypeInfo) {
+                                  sizeof (IdeLspPluginRenameProviderClass),
+                                  NULL,
+                                  NULL,
+                                  (GClassInitFunc)ide_lsp_plugin_rename_provider_class_init,
+                                  NULL,
+                                  ide_lsp_plugin_info_ref (info),
+                                  sizeof (IdeLspRenameProvider),
+                                  0,
+                                  NULL,
+                                  NULL,
+                                },
+                                G_TYPE_FLAG_FINAL);
+      g_type_add_interface_static (info->rename_provider_type,
+                                   IDE_TYPE_RENAME_PROVIDER,
+                                   &(GInterfaceInfo) {
+                                     (GInterfaceInitFunc)(void (*) 
(void))ide_lsp_plugin_rename_provider_iface_init,
+                                     NULL,
+                                     NULL,
+                                   });
+    }
+
+  return g_object_newv (info->rename_provider_type, n_parameters, parameters);
+}
+G_GNUC_END_IGNORE_DEPRECATIONS
diff --git a/src/libide/lsp/ide-lsp-plugin-symbol-resolver.c b/src/libide/lsp/ide-lsp-plugin-symbol-resolver.c
new file mode 100644
index 000000000..34fa42747
--- /dev/null
+++ b/src/libide/lsp/ide-lsp-plugin-symbol-resolver.c
@@ -0,0 +1,100 @@
+/* ide-lsp-plugin-symbol-resolver.c
+ *
+ * Copyright 2022 Christian Hergert <chergert redhat 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/>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-or-later
+ */
+
+#define G_LOG_DOMAIN "ide-lsp-plugin-symbol-resolver"
+
+#include "config.h"
+
+#include <libpeas/peas.h>
+
+#include <libide-code.h>
+
+#include "ide-lsp-symbol-resolver.h"
+#include "ide-lsp-plugin-private.h"
+#include "ide-lsp-service.h"
+
+typedef struct _IdeLspPluginSymbolResolverClass
+{
+  IdeLspSymbolResolverClass  parent_class;
+  IdeLspPluginInfo              *info;
+} IdeLspPluginSymbolResolverClass;
+
+static void
+ide_lsp_plugin_symbol_resolver_load (IdeSymbolResolver *resolver)
+{
+  IdeLspPluginSymbolResolverClass *klass = (IdeLspPluginSymbolResolverClass *)(((GTypeInstance 
*)resolver)->g_class);
+  g_autoptr(IdeLspServiceClass) service_class = g_type_class_ref (klass->info->service_type);
+
+  ide_lsp_service_class_bind_client (service_class, IDE_OBJECT (resolver));
+}
+
+static void
+ide_lsp_plugin_symbol_resolver_class_init (IdeLspPluginSymbolResolverClass *klass,
+                                           IdeLspPluginInfo                    *info)
+{
+  klass->info = info;
+}
+
+static void
+ide_lsp_plugin_symbol_resolver_iface_init (IdeSymbolResolverInterface *iface)
+{
+  iface->load = ide_lsp_plugin_symbol_resolver_load;
+}
+
+G_GNUC_BEGIN_IGNORE_DEPRECATIONS
+GObject *
+ide_lsp_plugin_create_symbol_resolver (guint             n_parameters,
+                                       GParameter       *parameters,
+                                       IdeLspPluginInfo *info)
+{
+  ide_lsp_plugin_remove_plugin_info_param (&n_parameters, parameters);
+
+  if G_UNLIKELY (info->symbol_resolver_type == G_TYPE_INVALID)
+    {
+      g_autofree char *name = g_strconcat (info->module_name, "+SymbolResolver", NULL);
+
+      info->symbol_resolver_type =
+        g_type_register_static (IDE_TYPE_LSP_SYMBOL_RESOLVER,
+                                name,
+                                &(GTypeInfo) {
+                                  sizeof (IdeLspPluginSymbolResolverClass),
+                                  NULL,
+                                  NULL,
+                                  (GClassInitFunc)ide_lsp_plugin_symbol_resolver_class_init,
+                                  NULL,
+                                  ide_lsp_plugin_info_ref (info),
+                                  sizeof (IdeLspSymbolResolver),
+                                  0,
+                                  NULL,
+                                  NULL,
+                                },
+                                G_TYPE_FLAG_FINAL);
+      g_type_add_interface_static (info->symbol_resolver_type,
+                                   IDE_TYPE_SYMBOL_RESOLVER,
+                                   &(GInterfaceInfo) {
+                                     (GInterfaceInitFunc)(void (*) 
(void))ide_lsp_plugin_symbol_resolver_iface_init,
+                                     NULL,
+                                     NULL,
+                                   });
+    }
+
+  return g_object_newv (info->symbol_resolver_type, n_parameters, parameters);
+}
+G_GNUC_END_IGNORE_DEPRECATIONS
diff --git a/src/libide/lsp/ide-lsp-plugin.c b/src/libide/lsp/ide-lsp-plugin.c
new file mode 100644
index 000000000..1d6bedc40
--- /dev/null
+++ b/src/libide/lsp/ide-lsp-plugin.c
@@ -0,0 +1,354 @@
+/* ide-lsp-plugin.c
+ *
+ * Copyright 2022 Christian Hergert <chergert redhat 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/>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-or-later
+ */
+
+#define G_LOG_DOMAIN "ide-lsp-plugin"
+
+#include "config.h"
+
+#include "ide-lsp-diagnostic-provider.h"
+#include "ide-lsp-symbol-resolver.h"
+#include "ide-lsp-highlighter.h"
+#include "ide-lsp-formatter.h"
+#include "ide-lsp-hover-provider.h"
+#include "ide-lsp-rename-provider.h"
+#include "ide-lsp-code-action-provider.h"
+#include "ide-lsp-plugin-private.h"
+#include "ide-lsp-service.h"
+
+typedef enum _IdeLspPluginFeatures
+{
+  IDE_LSP_PLUGIN_FEATURES_DIAGNOSTICS     = 1 << 0,
+  IDE_LSP_PLUGIN_FEATURES_COMPLETION      = 1 << 1,
+  IDE_LSP_PLUGIN_FEATURES_SYMBOL_RESOLVER = 1 << 2,
+  IDE_LSP_PLUGIN_FEATURES_HIGHLIGHTER     = 1 << 3,
+  IDE_LSP_PLUGIN_FEATURES_FORMATTER       = 1 << 4,
+  IDE_LSP_PLUGIN_FEATURES_HOVER           = 1 << 5,
+  IDE_LSP_PLUGIN_FEATURES_RENAME          = 1 << 6,
+  IDE_LSP_PLUGIN_FEATURES_CODE_ACTION     = 1 << 7,
+  IDE_LSP_PLUGIN_FEATURES_ALL = ~0,
+} IdeLspPluginFeatures;
+
+IdeLspPluginInfo *
+ide_lsp_plugin_info_ref (IdeLspPluginInfo *self)
+{
+  return g_atomic_rc_box_acquire (self);
+}
+
+static void
+ide_lsp_plugin_info_finalize (gpointer data)
+{
+  IdeLspPluginInfo *self = data;
+
+  g_clear_pointer (&self->command, g_strfreev);
+  g_clear_pointer (&self->languages, g_strfreev);
+  g_clear_pointer (&self->default_settings, g_bytes_unref);
+  g_clear_pointer (&self->module_name, g_free);
+}
+
+void
+ide_lsp_plugin_info_unref (IdeLspPluginInfo *info)
+{
+  g_atomic_rc_box_release_full (info, ide_lsp_plugin_info_finalize);
+}
+
+static IdeLspPluginInfo *
+ide_lsp_plugin_info_new (void)
+{
+  return g_atomic_rc_box_new0 (IdeLspPluginInfo);
+}
+
+G_DEFINE_AUTOPTR_CLEANUP_FUNC (IdeLspPluginInfo, ide_lsp_plugin_info_unref)
+
+static GBytes *
+load_bytes (const char *path)
+{
+  if (path == NULL)
+    {
+      return NULL;
+    }
+  else if (g_str_has_prefix (path, "resource://"))
+    {
+      return g_resources_lookup_data (path, 0, NULL);
+    }
+  else
+    {
+      g_autoptr(GFile) file = g_file_new_for_path (path);
+      return g_file_load_bytes (file, NULL, NULL, NULL);
+    }
+}
+
+typedef struct _IdeLspPluginService
+{
+  IdeLspService parent_instance;
+} IdeLspPluginService;
+
+typedef struct _IdeLspPluginServiceClass
+{
+  IdeLspServiceClass  parent_class;
+  IdeLspPluginInfo   *info;
+} IdeLspPluginServiceClass;
+
+static void
+ide_lsp_plugin_service_configure_client (IdeLspService *service,
+                                         IdeLspClient  *client)
+{
+  IdeLspPluginService *self = (IdeLspPluginService *)service;
+  IdeLspPluginServiceClass *klass = (IdeLspPluginServiceClass *)((GTypeInstance *)service)->g_class;
+  g_autoptr(GVariant) options = NULL;
+  IdeContext *context;
+
+  g_assert (IDE_IS_LSP_SERVICE (self));
+  g_assert (IDE_IS_LSP_CLIENT (client));
+
+  if (klass->info->languages != NULL)
+    {
+      for (guint i = 0; klass->info->languages[i]; i++)
+        ide_lsp_client_add_language (client, klass->info->languages[i]);
+    }
+
+  if (!(context = ide_object_get_context (IDE_OBJECT (service))))
+    return;
+
+  /* TODO: User configurable settings */
+
+#if 0
+  if (klass->info->default_settings)
+    ide_lsp_client_set_initialization_options (client, klass->data->default_settings);
+#endif
+}
+
+static void
+ide_lsp_plugin_service_prepare_run_context (IdeLspService *service,
+                                            IdePipeline   *pipeline,
+                                            IdeRunContext *run_context)
+{
+  IdeLspPluginServiceClass *klass;
+
+  g_assert (IDE_IS_LSP_SERVICE (service));
+  g_assert (!pipeline || IDE_IS_PIPELINE (pipeline));
+  g_assert (IDE_IS_RUN_CONTEXT (run_context));
+
+  klass = (IdeLspPluginServiceClass *)G_OBJECT_GET_CLASS (service);
+
+  if (klass->info->command[0] && klass->info->command[1])
+    ide_run_context_append_args (run_context, (const char * const *)&klass->info->command[1]);
+}
+
+static void
+ide_lsp_plugin_service_class_init (IdeLspPluginServiceClass *klass,
+                                   IdeLspPluginInfo         *info)
+{
+  IdeLspServiceClass *service_class = IDE_LSP_SERVICE_CLASS (klass);
+
+  klass->info = info;
+
+  service_class->configure_client = ide_lsp_plugin_service_configure_client;
+  service_class->prepare_run_context = ide_lsp_plugin_service_prepare_run_context;
+}
+
+static void
+ide_lsp_plugin_service_init (IdeLspPluginService      *self,
+                             IdeLspPluginServiceClass *klass)
+{
+  ide_lsp_service_set_program (IDE_LSP_SERVICE (self), klass->info->command[0]);
+}
+
+static GType
+ide_lsp_plugin_register_service_gtype (IdeLspPluginInfo *info)
+{
+  g_autofree char *type_name = g_strconcat (info->module_name, "+IdeLspPluginService", NULL);
+  GTypeInfo type_info =
+  {
+    sizeof (IdeLspPluginServiceClass),
+    NULL,
+    NULL,
+    (GClassInitFunc)ide_lsp_plugin_service_class_init,
+    NULL,
+    ide_lsp_plugin_info_ref (info),
+    sizeof (IdeLspPluginService),
+    0,
+    (GInstanceInitFunc)ide_lsp_plugin_service_init,
+    NULL,
+  };
+  return g_type_register_static (IDE_TYPE_LSP_SERVICE, type_name, &type_info, G_TYPE_FLAG_FINAL);
+}
+
+static void
+ide_lsp_plugin_register (PeasObjectModule     *object_module,
+                         IdeLspPluginFeatures  features,
+                         const char * const   *command,
+                         const char * const   *languages,
+                         GBytes               *default_settings)
+{
+  g_autoptr(IdeLspPluginInfo) info = NULL;
+
+  g_return_if_fail (PEAS_IS_OBJECT_MODULE (object_module));
+
+  info = ide_lsp_plugin_info_new ();
+  info->module_name = g_strdup (peas_object_module_get_module_name (object_module));
+  info->command = g_strdupv ((char **)command);
+  info->languages = g_strdupv ((char **)languages);
+  info->default_settings = default_settings ? g_bytes_ref (default_settings) : NULL;
+  info->service_type = ide_lsp_plugin_register_service_gtype (info);
+
+  g_log (info->module_name,
+         G_LOG_LEVEL_DEBUG,
+         "Registered type %s",
+         g_type_name (info->service_type));
+
+  if ((features & IDE_LSP_PLUGIN_FEATURES_DIAGNOSTICS) != 0)
+    peas_object_module_register_extension_factory (object_module,
+                                                   IDE_TYPE_DIAGNOSTIC_PROVIDER,
+                                                   
(PeasFactoryFunc)ide_lsp_plugin_create_diagnostic_provider,
+                                                   ide_lsp_plugin_info_ref (info),
+                                                   (GDestroyNotify)ide_lsp_plugin_info_unref);
+
+  if ((features & IDE_LSP_PLUGIN_FEATURES_COMPLETION) != 0)
+    peas_object_module_register_extension_factory (object_module,
+                                                   GTK_SOURCE_TYPE_COMPLETION_PROVIDER,
+                                                   
(PeasFactoryFunc)ide_lsp_plugin_create_completion_provider,
+                                                   ide_lsp_plugin_info_ref (info),
+                                                   (GDestroyNotify)ide_lsp_plugin_info_unref);
+
+  if ((features & IDE_LSP_PLUGIN_FEATURES_SYMBOL_RESOLVER) != 0)
+    peas_object_module_register_extension_factory (object_module,
+                                                   IDE_TYPE_SYMBOL_RESOLVER,
+                                                   (PeasFactoryFunc)ide_lsp_plugin_create_symbol_resolver,
+                                                   ide_lsp_plugin_info_ref (info),
+                                                   (GDestroyNotify)ide_lsp_plugin_info_unref);
+
+  if ((features & IDE_LSP_PLUGIN_FEATURES_HIGHLIGHTER) != 0)
+    peas_object_module_register_extension_factory (object_module,
+                                                   IDE_TYPE_HIGHLIGHTER,
+                                                   (PeasFactoryFunc)ide_lsp_plugin_create_highlighter,
+                                                   ide_lsp_plugin_info_ref (info),
+                                                   (GDestroyNotify)ide_lsp_plugin_info_unref);
+
+  if ((features & IDE_LSP_PLUGIN_FEATURES_FORMATTER) != 0)
+    peas_object_module_register_extension_factory (object_module,
+                                                   IDE_TYPE_FORMATTER,
+                                                   (PeasFactoryFunc)ide_lsp_plugin_create_formatter,
+                                                   ide_lsp_plugin_info_ref (info),
+                                                   (GDestroyNotify)ide_lsp_plugin_info_unref);
+
+  if ((features & IDE_LSP_PLUGIN_FEATURES_HOVER) != 0)
+    peas_object_module_register_extension_factory (object_module,
+                                                   GTK_SOURCE_TYPE_HOVER_PROVIDER,
+                                                   (PeasFactoryFunc)ide_lsp_plugin_create_hover_provider,
+                                                   ide_lsp_plugin_info_ref (info),
+                                                   (GDestroyNotify)ide_lsp_plugin_info_unref);
+
+  if ((features & IDE_LSP_PLUGIN_FEATURES_RENAME) != 0)
+    peas_object_module_register_extension_factory (object_module,
+                                                   IDE_TYPE_RENAME_PROVIDER,
+                                                   (PeasFactoryFunc)ide_lsp_plugin_create_rename_provider,
+                                                   ide_lsp_plugin_info_ref (info),
+                                                   (GDestroyNotify)ide_lsp_plugin_info_unref);
+
+  if ((features & IDE_LSP_PLUGIN_FEATURES_CODE_ACTION) != 0)
+    peas_object_module_register_extension_factory (object_module,
+                                                   IDE_TYPE_CODE_ACTION_PROVIDER,
+                                                   
(PeasFactoryFunc)ide_lsp_plugin_create_code_action_provider,
+                                                   ide_lsp_plugin_info_ref (info),
+                                                   (GDestroyNotify)ide_lsp_plugin_info_unref);
+}
+
+void
+ide_lsp_plugin_register_types (PeasObjectModule *object_module)
+{
+  g_autofree char *x_lsp_languages = NULL;
+  g_autofree char *settings_path = NULL;
+  g_autoptr(GBytes) default_settings = NULL;
+  g_autoptr(GError) error = NULL;
+  g_auto(GStrv) argv = NULL;
+  g_auto(GStrv) languages = NULL;
+  PeasPluginInfo *plugin_info;
+  PeasEngine *engine;
+  const char *data_dir;
+  const char *module_name;
+  const char *x_lsp_command;
+  const char *x_lsp_settings;
+  int argc;
+
+  g_return_if_fail (PEAS_IS_OBJECT_MODULE (object_module));
+
+  engine = peas_engine_get_default ();
+  module_name = peas_object_module_get_module_name (object_module);
+  plugin_info = peas_engine_get_plugin_info (engine, module_name);
+  x_lsp_command = peas_plugin_info_get_external_data (plugin_info, "LSP-Command");
+  x_lsp_languages = g_strdup (peas_plugin_info_get_external_data (plugin_info, "LSP-Languages"));
+  x_lsp_settings = peas_plugin_info_get_external_data (plugin_info, "LSP-Settings");
+  data_dir = peas_plugin_info_get_data_dir (plugin_info);
+
+  if (x_lsp_command == NULL)
+    {
+      g_critical ("Plugin %s missing X-LSP-Command=", module_name);
+      return;
+    }
+
+  if (x_lsp_languages == NULL)
+    {
+      g_critical ("Plugin %s missing X-LSP-Languages=", module_name);
+      return;
+    }
+
+  languages = g_strsplit (g_strdelimit (x_lsp_languages, ",", ';'), ";", 0);
+
+  if (!g_shell_parse_argv (x_lsp_command, &argc, &argv, &error))
+    {
+      g_critical ("Plugin %s provides invalid X-LSP-Command=%s: %s",
+                  module_name, x_lsp_command, error->message);
+      return;
+    }
+
+  if (x_lsp_settings == NULL)
+    x_lsp_settings = "settings.json";
+
+  settings_path = g_build_filename (data_dir, x_lsp_settings, NULL);
+  default_settings = load_bytes (settings_path);
+
+  ide_lsp_plugin_register (object_module,
+                           IDE_LSP_PLUGIN_FEATURES_ALL,
+                           (const char * const *)argv,
+                           (const char * const *)languages,
+                           default_settings);
+}
+
+G_GNUC_BEGIN_IGNORE_DEPRECATIONS
+void
+ide_lsp_plugin_remove_plugin_info_param (guint      *n_parameters,
+                                         GParameter *parameters)
+{
+  static GType plugin_info_type;
+  const GParameter *param;
+
+  if (*n_parameters == 0)
+    return;
+
+  if G_UNLIKELY (plugin_info_type == G_TYPE_INVALID)
+    plugin_info_type = PEAS_TYPE_PLUGIN_INFO;
+
+  param = &parameters[(*n_parameters) - 1];
+
+  if (G_VALUE_TYPE (&param->value) == plugin_info_type &&
+      strcmp (param->name, "plugin-info") == 0)
+    (*n_parameters)--;
+}
+G_GNUC_BEGIN_IGNORE_DEPRECATIONS
diff --git a/src/libide/lsp/ide-lsp-plugin.h b/src/libide/lsp/ide-lsp-plugin.h
new file mode 100644
index 000000000..6c2284632
--- /dev/null
+++ b/src/libide/lsp/ide-lsp-plugin.h
@@ -0,0 +1,32 @@
+/* ide-lsp-plugin.h
+ *
+ * Copyright 2022 Christian Hergert <chergert redhat 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/>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-or-later
+ */
+
+#pragma once
+
+#include <libpeas/peas.h>
+
+#include <libide-core.h>
+
+G_BEGIN_DECLS
+
+IDE_AVAILABLE_IN_44
+void ide_lsp_plugin_register_types (PeasObjectModule *object_module);
+
+G_END_DECLS
diff --git a/src/libide/lsp/libide-lsp.h b/src/libide/lsp/libide-lsp.h
index 765bd7176..362fad933 100644
--- a/src/libide/lsp/libide-lsp.h
+++ b/src/libide/lsp/libide-lsp.h
@@ -38,6 +38,7 @@
 #include "ide-lsp-formatter.h"
 #include "ide-lsp-highlighter.h"
 #include "ide-lsp-hover-provider.h"
+#include "ide-lsp-plugin.h"
 #include "ide-lsp-rename-provider.h"
 #include "ide-lsp-search-provider.h"
 #include "ide-lsp-service.h"
diff --git a/src/libide/lsp/meson.build b/src/libide/lsp/meson.build
index f623287d5..0073aa457 100644
--- a/src/libide/lsp/meson.build
+++ b/src/libide/lsp/meson.build
@@ -19,6 +19,7 @@ libide_lsp_public_headers = [
   'ide-lsp-formatter.h',
   'ide-lsp-highlighter.h',
   'ide-lsp-hover-provider.h',
+  'ide-lsp-plugin.h',
   'ide-lsp-rename-provider.h',
   'ide-lsp-search-provider.h',
   'ide-lsp-service.h',
@@ -31,6 +32,7 @@ libide_lsp_public_headers = [
 ]
 
 libide_lsp_private_headers = [
+  'ide-lsp-plugin-private.h',
   'ide-lsp-symbol-node-private.h',
   'ide-lsp-symbol-tree-private.h',
 ]
@@ -53,6 +55,15 @@ libide_lsp_public_sources = [
   'ide-lsp-formatter.c',
   'ide-lsp-highlighter.c',
   'ide-lsp-hover-provider.c',
+  'ide-lsp-plugin.c',
+  'ide-lsp-plugin-code-action-provider.c',
+  'ide-lsp-plugin-completion-provider.c',
+  'ide-lsp-plugin-diagnostic-provider.c',
+  'ide-lsp-plugin-formatter.c',
+  'ide-lsp-plugin-highlighter.c',
+  'ide-lsp-plugin-hover-provider.c',
+  'ide-lsp-plugin-rename-provider.c',
+  'ide-lsp-plugin-symbol-resolver.c',
   'ide-lsp-rename-provider.c',
   'ide-lsp-search-provider.c',
   'ide-lsp-search-result.c',


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