[gnome-builder] LSP: add full text synchronization alternative for didChange signal



commit 3381f396eb69ef48570cd20700a1affa63e6ce24
Author: Günther Wagner <info gunibert de>
Date:   Sun Apr 26 11:52:39 2020 +0200

    LSP: add full text synchronization alternative for didChange signal

 meson_options.txt                                 |  2 +-
 src/libide/lsp/ide-lsp-client.c                   | 97 +++++++++++++++++------
 src/plugins/rust-analyzer/rust_analyzer_plugin.py | 48 +++++------
 3 files changed, 97 insertions(+), 50 deletions(-)
---
diff --git a/meson_options.txt b/meson_options.txt
index f187c821f..0a103b57e 100644
--- a/meson_options.txt
+++ b/meson_options.txt
@@ -64,7 +64,7 @@ option('plugin_python_pack', type: 'boolean')
 option('plugin_qemu', type: 'boolean')
 option('plugin_quick_highlight', type: 'boolean')
 option('plugin_retab', type: 'boolean')
-option('plugin_rls', type: 'boolean', value: false)
+option('plugin_rls', type: 'boolean')
 option('plugin_rust_analyzer', type: 'boolean')
 option('plugin_rustup', type: 'boolean')
 option('plugin_shellcmd', type: 'boolean')
diff --git a/src/libide/lsp/ide-lsp-client.c b/src/libide/lsp/ide-lsp-client.c
index 927a8ea05..fb4e85605 100644
--- a/src/libide/lsp/ide-lsp-client.c
+++ b/src/libide/lsp/ide-lsp-client.c
@@ -68,6 +68,12 @@ enum {
   SEVERITY_HINT        = 4,
 };
 
+enum {
+  TEXT_DOCUMENT_SYNC_NONE,
+  TEXT_DOCUMENT_SYNC_FULL,
+  TEXT_DOCUMENT_SYNC_INCREMENTAL,
+};
+
 enum {
   PROP_0,
   PROP_IO_STREAM,
@@ -208,10 +214,9 @@ ide_lsp_client_buffer_insert_text (IdeLspClient *self,
 {
   g_autoptr(GVariant) params = NULL;
   g_autofree gchar *uri = NULL;
-  g_autofree gchar *copy = NULL;
+  GVariant *capabilities = NULL;
   gint64 version;
-  gint line;
-  gint column;
+  gint64 text_document_sync = TEXT_DOCUMENT_SYNC_NONE;
 
   IDE_ENTRY;
 
@@ -220,37 +225,79 @@ ide_lsp_client_buffer_insert_text (IdeLspClient *self,
   g_assert (location != NULL);
   g_assert (IDE_IS_BUFFER (buffer));
 
-  copy = g_strndup (new_text, len);
+  capabilities = ide_lsp_client_get_server_capabilities (self);
+  if (capabilities != NULL) {
+    gint64 tds = 0;
+
+    // for backwards compatibility reasons LS can stick to a number instead of the structure
+    if (JSONRPC_MESSAGE_PARSE (capabilities, "textDocumentSync", JSONRPC_MESSAGE_GET_INT64 (&tds))
+        | JSONRPC_MESSAGE_PARSE (capabilities, "textDocumentSync", "{", "change", JSONRPC_MESSAGE_GET_INT64 
(&tds), "}"))
+      {
+        text_document_sync = tds;
+      }
+  }
 
   uri = ide_buffer_dup_uri (buffer);
 
   /* We get called before this change is registered */
   version = (gint64)ide_buffer_get_change_count (buffer) + 1;
 
-  line = gtk_text_iter_get_line (location);
-  column = gtk_text_iter_get_line_offset (location);
+  if (text_document_sync == TEXT_DOCUMENT_SYNC_INCREMENTAL)
+    {
+      g_autofree gchar *copy = NULL;
+      gint line;
+      gint column;
 
-  params = JSONRPC_MESSAGE_NEW (
-    "textDocument", "{",
-      "uri", JSONRPC_MESSAGE_PUT_STRING (uri),
-      "version", JSONRPC_MESSAGE_PUT_INT64 (version),
-    "}",
-    "contentChanges", "[",
-      "{",
-        "range", "{",
-          "start", "{",
-            "line", JSONRPC_MESSAGE_PUT_INT64 (line),
-            "character", JSONRPC_MESSAGE_PUT_INT64 (column),
-          "}",
-          "end", "{",
-            "line", JSONRPC_MESSAGE_PUT_INT64 (line),
-            "character", JSONRPC_MESSAGE_PUT_INT64 (column),
+      copy = g_strndup (new_text, len);
+
+      line = gtk_text_iter_get_line (location);
+      column = gtk_text_iter_get_line_offset (location);
+
+      params = JSONRPC_MESSAGE_NEW (
+        "textDocument", "{",
+          "uri", JSONRPC_MESSAGE_PUT_STRING (uri),
+          "version", JSONRPC_MESSAGE_PUT_INT64 (version),
+        "}",
+        "contentChanges", "[",
+          "{",
+            "range", "{",
+              "start", "{",
+                "line", JSONRPC_MESSAGE_PUT_INT64 (line),
+                "character", JSONRPC_MESSAGE_PUT_INT64 (column),
+              "}",
+              "end", "{",
+                "line", JSONRPC_MESSAGE_PUT_INT64 (line),
+                "character", JSONRPC_MESSAGE_PUT_INT64 (column),
+              "}",
+            "}",
+            "rangeLength", JSONRPC_MESSAGE_PUT_INT64 (0),
+            "text", JSONRPC_MESSAGE_PUT_STRING (copy),
           "}",
+        "]");
+    }
+  else if (text_document_sync == TEXT_DOCUMENT_SYNC_FULL)
+    {
+      g_autoptr(GBytes) content = NULL;
+      const gchar *text;
+      g_autoptr(GString) str = NULL;
+
+      content = ide_buffer_dup_content (buffer);
+      text = (const gchar *)g_bytes_get_data (content, NULL);
+      str = g_string_new (text);
+      g_string_insert_len (str, gtk_text_iter_get_offset (location), new_text, len);
+
+      params = JSONRPC_MESSAGE_NEW (
+        "textDocument", "{",
+          "uri", JSONRPC_MESSAGE_PUT_STRING (uri),
+          "version", JSONRPC_MESSAGE_PUT_INT64 (version),
         "}",
-        "rangeLength", JSONRPC_MESSAGE_PUT_INT64 (0),
-        "text", JSONRPC_MESSAGE_PUT_STRING (copy),
-      "}",
-    "]");
+        "contentChanges", "[",
+          "{",
+            "text", JSONRPC_MESSAGE_PUT_STRING (str->str),
+          "}",
+        "]");
+    }
+
 
   ide_lsp_client_send_notification_async (self,
                                           "textDocument/didChange",
diff --git a/src/plugins/rust-analyzer/rust_analyzer_plugin.py 
b/src/plugins/rust-analyzer/rust_analyzer_plugin.py
index 93dff63e2..eeec191b1 100644
--- a/src/plugins/rust-analyzer/rust_analyzer_plugin.py
+++ b/src/plugins/rust-analyzer/rust_analyzer_plugin.py
@@ -33,7 +33,7 @@ from gi.repository import Ide
 
 DEV_MODE = False
 
-class RlsService(Ide.Object):
+class RustAnalyzerService(Ide.Object):
     _client = None
     _has_started = False
     _supervisor = None
@@ -41,7 +41,7 @@ class RlsService(Ide.Object):
 
     @classmethod
     def from_context(klass, context):
-        return context.ensure_child_typed(RlsService)
+        return context.ensure_child_typed(RustAnalyzerService)
 
     @GObject.Property(type=Ide.LspClient)
     def client(self):
@@ -86,8 +86,8 @@ class RlsService(Ide.Object):
 
     def do_stop(self):
         """
-        Stops the Rust Language Server upon request to shutdown the
-        RlsService.
+        Stops the Rust Analyzer Language Server upon request to shutdown the
+        RustAnalyzerService.
         """
         if self._monitor is not None:
             monitor, self._monitor = self._monitor, None
@@ -106,9 +106,9 @@ class RlsService(Ide.Object):
         Ide.SubprocessSupervisor.
 
         Various extension points (diagnostics, symbol providers, etc) use
-        the RlsService to access the rust components they need.
+        the RustAnalyzerService to access the rust components they need.
         """
-        # To avoid starting the `rust-analyzer-linux` process unconditionally at startup,
+        # To avoid starting the `rust-analyzer` 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:
@@ -130,7 +130,7 @@ class RlsService(Ide.Object):
 
             # If rls was installed with Cargo, try to discover that
             # to save the user having to update PATH.
-            path_to_rust_analyzer_bin = os.path.expanduser("~/.cargo/bin/rust-analyzer-linux")
+            path_to_rust_analyzer_bin = os.path.expanduser("~/.cargo/bin/rust-analyzer")
             if os.path.exists(path_to_rust_analyzer_bin):
                 old_path = os.getenv('PATH')
                 new_path = os.path.expanduser('~/.cargo/bin')
@@ -138,11 +138,11 @@ class RlsService(Ide.Object):
                     new_path += os.path.pathsep + old_path
                 launcher.setenv('PATH', new_path, True)
             else:
-                path_to_rls = "rust-analyzer-linux"
+                path_to_rust_analyzer_bin = "rust-analyzer"
 
             # Setup our Argv. We want to communicate over STDIN/STDOUT,
             # so it does not require any command line options.
-            launcher.push_argv(path_to_rls)
+            launcher.push_argv(path_to_rust_analyzer_bin)
 
             # Spawn our peer process and monitor it for
             # crashes. We may need to restart it occasionally.
@@ -214,41 +214,41 @@ class RlsService(Ide.Object):
         our `rls` process has crashed.
         """
         context = provider.get_context()
-        self = RlsService.from_context(context)
+        self = RustAnalyzerService.from_context(context)
         self._ensure_started()
         self.bind_property('client', provider, 'client', GObject.BindingFlags.SYNC_CREATE)
 
-class RlsDiagnosticProvider(Ide.LspDiagnosticProvider, Ide.DiagnosticProvider):
+class RustAnalyzerDiagnosticProvider(Ide.LspDiagnosticProvider, Ide.DiagnosticProvider):
     def do_load(self):
-        RlsService.bind_client(self)
+        RustAnalyzerService.bind_client(self)
 
-class RlsCompletionProvider(Ide.LspCompletionProvider, Ide.CompletionProvider):
+class RustAnalyzerCompletionProvider(Ide.LspCompletionProvider, Ide.CompletionProvider):
     def do_load(self, context):
-        RlsService.bind_client(self)
+        RustAnalyzerService.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 RlsRenameProvider(Ide.LspRenameProvider, Ide.RenameProvider):
+class RustAnalyzerRenameProvider(Ide.LspRenameProvider, Ide.RenameProvider):
     def do_load(self):
-        RlsService.bind_client(self)
+        RustAnalyzerService.bind_client(self)
 
-class RlsSymbolResolver(Ide.LspSymbolResolver, Ide.SymbolResolver):
+class RustAnalyzerSymbolResolver(Ide.LspSymbolResolver, Ide.SymbolResolver):
     def do_load(self):
-        RlsService.bind_client(self)
+        RustAnalyzerService.bind_client(self)
 
-class RlsHighlighter(Ide.LspHighlighter, Ide.Highlighter):
+class RustAnalyzerHighlighter(Ide.LspHighlighter, Ide.Highlighter):
     def do_load(self):
-        RlsService.bind_client(self)
+        RustAnalyzerService.bind_client(self)
 
-class RlsFormatter(Ide.LspFormatter, Ide.Formatter):
+class RustAnalyzerFormatter(Ide.LspFormatter, Ide.Formatter):
     def do_load(self):
-        RlsService.bind_client(self)
+        RustAnalyzerService.bind_client(self)
 
-class RlsHoverProvider(Ide.LspHoverProvider, Ide.HoverProvider):
+class RustAnalyzerHoverProvider(Ide.LspHoverProvider, Ide.HoverProvider):
     def do_prepare(self):
         self.props.category = 'Rust'
         self.props.priority = 200
-        RlsService.bind_client(self)
+        RustAnalyzerService.bind_client(self)


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