Subject: [PATCH v4 25/31] gnu: Add rust-tree-sitter-for-emacs.

From: Pierre Langlois <[email-masked]>
Date: Fri, 18 Feb 2022 14:39:42 +0000

* gnu/packages/tree-sitter.scm (rust-tree-sitter-for-emacs): New variable.
* gnu/packages/patches/rust-tree-sitter-text-provider-fix.patch: New file.
* gnu/local.mk (dist_patch_DATA): Register it.
---
 gnu/local.mk                                  |  1 +
 .../rust-tree-sitter-text-provider-fix.patch  | 98 +++++++++++++++++++
 gnu/packages/tree-sitter.scm                  | 45 +++++++++
 3 files changed, 144 insertions(+)
 create mode 100644 gnu/packages/patches/rust-tree-sitter-text-provider-fix.patch

diff --git a/gnu/local.mk b/gnu/local.mk
index 97ab160c3c..0c113956d3 100644
--- a/gnu/local.mk
+++ b/gnu/local.mk
@@ -1775,6 +1775,7 @@ dist_patch_DATA =						\
   %D%/packages/patches/rust-nettle-sys-disable-vendor.patch	 \
   %D%/packages/patches/rust-openssl-sys-no-vendor.patch	\
   %D%/packages/patches/rust-shell2batch-lint-fix.patch		\
+  %D%/packages/patches/rust-tree-sitter-text-provider-fix.patch	\
   %D%/packages/patches/rust-wl-clipboard-rs-newer-wl.patch      \
   %D%/packages/patches/sbc-fix-build-non-x86.patch		\
   %D%/packages/patches/sbcl-aserve-add-HTML-5-elements.patch	\
diff --git a/gnu/packages/patches/rust-tree-sitter-text-provider-fix.patch b/gnu/packages/patches/rust-tree-sitter-text-provider-fix.patch
new file mode 100644
index 0000000000..beda2d8391
--- /dev/null
+++ b/gnu/packages/patches/rust-tree-sitter-text-provider-fix.patch
@@ -0,0 +1,98 @@
+From 475b822f47bdc58d832533448b6f6d9818554f37 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Tu=E1=BA=A5n-Anh=20Nguy=E1=BB=85n?= <[email-masked]>
+Date: Sun, 25 Jul 2021 13:11:52 +0700
+Subject: [PATCH] Allow TextProvider's iterators to generate owned text
+
+---
+ binding_rust/lib.rs | 33 ++++++++++++++++++---------------
+ 1 file changed, 18 insertions(+), 15 deletions(-)
+
+diff --git a/lib/binding_rust/lib.rs b/lib/binding_rust/lib.rs
+index 068a60285..79c3991ed 100644
+--- a/binding_rust/lib.rs
++++ b/binding_rust/lib.rs
+@@ -8,6 +8,7 @@ pub mod allocations;
+ use std::os::unix::io::AsRawFd;
+
+ use std::{
++    borrow::Cow,
+     char, error,
+     ffi::CStr,
+     fmt, hash, iter,
+@@ -162,7 +163,8 @@ pub struct QueryCaptures<'a, 'tree: 'a, T: TextProvider<'a>> {
+ }
+
+ pub trait TextProvider<'a> {
+-    type I: Iterator<Item = &'a [u8]> + 'a;
++    type I: Iterator<Item = Cow<'a, [u8]>>;
++
+     fn text(&mut self, node: Node) -> Self::I;
+ }
+
+@@ -1799,19 +1801,19 @@ impl<'a, 'tree> QueryMatch<'a, 'tree> {
+         buffer2: &mut Vec<u8>,
+         text_provider: &mut impl TextProvider<'a>,
+     ) -> bool {
+-        fn get_text<'a, 'b: 'a, I: Iterator<Item = &'b [u8]>>(
++        fn get_text<'a, 'b: 'a, I: Iterator<Item = Cow<'b, [u8]>>>(
+             buffer: &'a mut Vec<u8>,
+             mut chunks: I,
+-        ) -> &'a [u8] {
+-            let first_chunk = chunks.next().unwrap_or(&[]);
++        ) -> Cow<'a, [u8]> {
++            let first_chunk = chunks.next().unwrap_or(Cow::Owned(vec![0u8; 0]));
+             if let Some(next_chunk) = chunks.next() {
+                 buffer.clear();
+-                buffer.extend_from_slice(first_chunk);
+-                buffer.extend_from_slice(next_chunk);
++                buffer.extend_from_slice(&first_chunk);
++                buffer.extend_from_slice(&next_chunk);
+                 for chunk in chunks {
+-                    buffer.extend_from_slice(chunk);
++                    buffer.extend_from_slice(&chunk);
+                 }
+-                buffer.as_slice()
++                Cow::Borrowed(buffer.as_slice())
+             } else {
+                 first_chunk
+             }
+@@ -1835,7 +1837,7 @@ impl<'a, 'tree> QueryMatch<'a, 'tree> {
+                 TextPredicate::CaptureMatchString(i, r, is_positive) => {
+                     let node = self.nodes_for_capture_index(*i).next().unwrap();
+                     let text = get_text(buffer1, text_provider.text(node));
+-                    r.is_match(text) == *is_positive
++                    r.is_match(&text) == *is_positive
+                 }
+             })
+     }
+@@ -1946,23 +1948,24 @@ impl<'cursor, 'tree> fmt::Debug for QueryMatch<'cursor, 'tree> {
+     }
+ }
+
+-impl<'a, F, I> TextProvider<'a> for F
++impl<'a, F, I, T> TextProvider<'a> for F
+ where
+     F: FnMut(Node) -> I,
+-    I: Iterator<Item = &'a [u8]> + 'a,
++    T: Into<Cow<'a, [u8]>>,
++    I: Iterator<Item = T>,
+ {
+-    type I = I;
++    type I = iter::Map<I, fn(T) -> Cow<'a, [u8]>>;
+
+     fn text(&mut self, node: Node) -> Self::I {
+-        (self)(node)
++        (self)(node).map(T::into)
+     }
+ }
+
+ impl<'a> TextProvider<'a> for &'a [u8] {
+-    type I = iter::Once<&'a [u8]>;
++    type I = iter::Once<Cow<'a, [u8]>>;
+
+     fn text(&mut self, node: Node) -> Self::I {
+-        iter::once(&self[node.byte_range()])
++        iter::once(Cow::Borrowed(&self[node.byte_range()]))
+     }
+ }
+
diff --git a/gnu/packages/tree-sitter.scm b/gnu/packages/tree-sitter.scm
index 309b38386d..3c8b4588c4 100644
--- a/gnu/packages/tree-sitter.scm
+++ b/gnu/packages/tree-sitter.scm
@@ -22,9 +22,11 @@ (define-module (gnu packages tree-sitter)
   #:use-module (guix build-system cargo)
   #:use-module (guix build-system gnu)
   #:use-module (guix build-system node)
+  #:use-module (guix download)
   #:use-module (guix git-download)
   #:use-module (guix packages)
   #:use-module (guix utils)
+  #:use-module (gnu packages)
   #:use-module (gnu packages algebra)
   #:use-module (gnu packages crates-graphics)
   #:use-module (gnu packages crates-io)
@@ -150,6 +152,49 @@ (define-public tree-sitter-cli
 This package includes the @command{tree-sitter} command-line tool.")
     (license license:expat)))

+;; We need to apply a patch in order to compile the rust bindings against the
+;; emacs tree-sitter module.
+;; See https://github.com/tree-sitter/tree-sitter/pull/1294
+(define-public rust-tree-sitter-for-emacs
+  (package
+    (name "rust-tree-sitter")
+    (version "0.20.0")
+    (source
+      (origin
+        (method url-fetch)
+        (uri (crate-uri "tree-sitter" version))
+        (file-name (string-append name "-" version ".tar.gz"))
+        (sha256
+         (base32
+          "1yg4p54hsfsxxknjq492i8b4rvibzpl2zdvr2bwvwakqgah05v33"))
+        (patches (search-patches "rust-tree-sitter-text-provider-fix.patch"))
+        (modules '((guix build utils)))
+        (snippet
+         '(begin
+            ;; Force
+            (delete-file-recursively "src")
+            (delete-file "binding_rust/build.rs")
+            (with-output-to-file "binding_rust/build.rs"
+              (lambda _
+                (format #t "fn main() {~@
+                        println!(\"cargo:rustc-link-lib=tree-sitter\");~@
+                        }~%")))
+            #f))))
+    (build-system cargo-build-system)
+    (inputs (list tree-sitter))
+    (arguments
+     `(#:tests? #f  ;; Running tests misinterprets comments as doc-tests.
+       #:cargo-inputs
+       (("rust-cc" ,rust-cc-1)
+        ("rust-lazy-static" ,rust-lazy-static-1)
+        ("rust-regex" ,rust-regex-1)
+        ("rust-spin" ,rust-spin-0.7))))
+    (home-page "https://tree-sitter.github.io/tree-sitter/")
+    (synopsis "Rust bindings to the Tree-sitter parsing library")
+    (description "This package provides Rust bindings to the Tree-sitter
+parsing library.")
+    (license license:expat)))
+
 (define tree-sitter-delete-generated-files
   '(begin
      (delete-file "binding.gyp")
--
2.34.0