From: Yuan Fu <casouri@gmail.com>
To: Stefan Monnier <monnier@iro.umontreal.ca>
Cc: "Clément Pit-Claudel" <cpitclaudel@gmail.com>,
emacs-devel <emacs-devel@gnu.org>
Subject: Re: How to add pseudo vector types
Date: Wed, 21 Jul 2021 23:10:14 -0400 [thread overview]
Message-ID: <F95D0E9B-0B21-450A-B91D-87E9E05873CE@gmail.com> (raw)
In-Reply-To: <DC82148D-FFEB-4B3E-B258-AC58D7124E16@gmail.com>
[-- Attachment #1: Type: text/plain, Size: 579 bytes --]
Here is another patch. No big progress since I’m busy moving this week. In this patch I changed from using change hooks to directly updating the trees in edit functions. I also added some node api and tests. Should I keep posting patches, or should I create a branch in /scratch? If the latter, how do I do it?
I’m aware of the ongoing enlightening discussion on potential optimizations for tree-sitter. My plan is to first complete the api and implement some minimal structural editing/font-lock features, then we can concretely measure what needs to improve.
Yuan
[-- Attachment #2: ts.3.patch --]
[-- Type: application/octet-stream, Size: 24276 bytes --]
From fd8ad36fe5ea3b9b12e80879b7434b8bc67b53db Mon Sep 17 00:00:00 2001
From: Yuan Fu <casouri@gmail.com>
Date: Wed, 21 Jul 2021 22:43:07 -0400
Subject: [PATCH] checkpoint 3
- change_hook -> directly in edit functions
- add a need_reparse field in Lisp_TS_Parser
- more node api
- tests
---
src/insdel.c | 43 ++++--
src/tree_sitter.c | 274 ++++++++++++++++++++++++++++------
src/tree_sitter.h | 18 ++-
test/src/tree-sitter-tests.el | 106 +++++++++++++
4 files changed, 377 insertions(+), 64 deletions(-)
create mode 100644 test/src/tree-sitter-tests.el
diff --git a/src/insdel.c b/src/insdel.c
index 3c1e13d38b..b313c50cda 100644
--- a/src/insdel.c
+++ b/src/insdel.c
@@ -947,6 +947,10 @@ insert_1_both (const char *string,
adjust_point (nchars, nbytes);
check_markers ();
+
+#ifdef HAVE_TREE_SITTER
+ ts_record_change (PT_BYTE - nbytes, PT_BYTE - nbytes, PT_BYTE);
+#endif
}
\f
/* Insert the part of the text of STRING, a Lisp object assumed to be
@@ -1078,6 +1082,10 @@ insert_from_string_1 (Lisp_Object string, ptrdiff_t pos, ptrdiff_t pos_byte,
adjust_point (nchars, outgoing_nbytes);
check_markers ();
+
+#ifdef HAVE_TREE_SITTER
+ ts_record_change (PT_BYTE - nbytes, PT_BYTE - nbytes, PT_BYTE);
+#endif
}
\f
/* Insert a sequence of NCHARS chars which occupy NBYTES bytes
@@ -1145,6 +1153,10 @@ insert_from_gap (ptrdiff_t nchars, ptrdiff_t nbytes, bool text_at_gap_tail)
adjust_point (nchars, nbytes);
check_markers ();
+
+#ifdef HAVE_TREE_SITTER
+ ts_record_change (PT_BYTE - nbytes, PT_BYTE - nbytes, nbytes);
+#endif
}
\f
/* Insert text from BUF, NCHARS characters starting at CHARPOS, into the
@@ -1292,6 +1304,11 @@ insert_from_buffer_1 (struct buffer *buf,
graft_intervals_into_buffer (intervals, PT, nchars, current_buffer, inherit);
adjust_point (nchars, outgoing_nbytes);
+
+#ifdef HAVE_TREE_SITTER
+ ts_record_change (PT_BYTE - outgoing_nbytes,
+ PT_BYTE - outgoing_nbytes, PT_BYTE);
+#endif
}
\f
/* Record undo information and adjust markers and position keepers for
@@ -1556,6 +1573,11 @@ replace_range (ptrdiff_t from, ptrdiff_t to, Lisp_Object new,
if (adjust_match_data)
update_search_regs (from, to, from + SCHARS (new));
+
+#ifdef HAVE_TREE_SITTER
+ ts_record_change (from_byte, to_byte, GPT_BYTE);
+#endif
+
signal_after_change (from, nchars_del, GPT - from);
update_compositions (from, GPT, CHECK_BORDER);
}
@@ -1683,6 +1705,11 @@ replace_range_2 (ptrdiff_t from, ptrdiff_t from_byte,
modiff_incr (&MODIFF);
CHARS_MODIFF = MODIFF;
+
+#ifdef HAVE_TREE_SITTER
+ ts_record_change (from_byte, to_byte, from_byte + insbytes);
+#endif
+
}
\f
/* Delete characters in current buffer
@@ -1893,6 +1920,10 @@ del_range_2 (ptrdiff_t from, ptrdiff_t from_byte,
evaporate_overlays (from);
+#ifdef HAVE_TREE_SITTER
+ ts_record_change (from_byte, to_byte, from_byte);
+#endif
+
return deletion;
}
@@ -2156,11 +2187,6 @@ signal_before_change (ptrdiff_t start_int, ptrdiff_t end_int,
run_hook (Qfirst_change_hook);
}
-#ifdef HAVE_TREE_SITTER
- /* FIXME: Is this the best place? */
- ts_before_change (start_int, end_int);
-#endif
-
/* Now run the before-change-functions if any. */
if (!NILP (Vbefore_change_functions))
{
@@ -2214,13 +2240,6 @@ signal_after_change (ptrdiff_t charpos, ptrdiff_t lendel, ptrdiff_t lenins)
if (inhibit_modification_hooks)
return;
-#ifdef HAVE_TREE_SITTER
- /* We disrespect combine-after-change, because if we don't record
- this change, the information that we need (the end byte position
- of the change) will be lost. */
- ts_after_change (charpos, lendel, lenins);
-#endif
-
/* If we are deferring calls to the after-change functions
and there are no before-change functions,
just record the args that we were going to use. */
diff --git a/src/tree_sitter.c b/src/tree_sitter.c
index 7d1225161c..a6a8912c84 100644
--- a/src/tree_sitter.c
+++ b/src/tree_sitter.c
@@ -32,49 +32,52 @@ Copyright (C) 2021 Free Software Foundation, Inc.
#include "coding.h"
#include "tree_sitter.h"
-/* parser.h defines a macro ADVANCE that conflicts with alloc.c. */
+/* parser.h defines a macro ADVANCE that conflicts with alloc.c. */
#include <tree_sitter/parser.h>
-/* Record the byte position of the end of the (to-be) changed text.
-We have to record it now, because by the time we get to after-change
-hook, the _byte_ position of the end is lost. */
-void
-ts_before_change (ptrdiff_t start_int, ptrdiff_t end_int)
+DEFUN ("tree-sitter-parser-p",
+ Ftree_sitter_parser_p, Stree_sitter_parser_p, 1, 1, 0,
+ doc: /* Return t if OBJECT is a tree-sitter parser. */)
+ (Lisp_Object object)
{
- /* Iterate through each parser in 'tree-sitter-parser-list' and
- record the byte position. There could be better ways to record
- it than storing the same position in every parser, but this is
- the most fool-proof way, and I expect a buffer to have only one
- parser most of the time anyway. */
- ptrdiff_t beg_byte = CHAR_TO_BYTE (start_int);
- ptrdiff_t old_end_byte = CHAR_TO_BYTE (end_int);
- Lisp_Object parser_list = Fsymbol_value (Qtree_sitter_parser_list);
- while (!NILP (parser_list))
- {
- Lisp_Object lisp_parser = Fcar (parser_list);
- XTS_PARSER (lisp_parser)->edit.start_byte = beg_byte;
- XTS_PARSER (lisp_parser)->edit.old_end_byte = old_end_byte;
- parser_list = Fcdr (parser_list);
- }
+ if (TS_PARSERP (object))
+ return Qt;
+ else
+ return Qnil;
+}
+
+DEFUN ("tree-sitter-node-p",
+ Ftree_sitter_node_p, Stree_sitter_node_p, 1, 1, 0,
+ doc: /* Return t if OBJECT is a tree-sitter node. */)
+ (Lisp_Object object)
+{
+ if (TS_NODEP (object))
+ return Qt;
+ else
+ return Qnil;
}
/* Update each parser's tree after the user made an edit. This
function does not parse the buffer and only updates the tree. (So it
should be very fast.) */
void
-ts_after_change (ptrdiff_t charpos, ptrdiff_t lendel, ptrdiff_t lenins)
+ts_record_change (ptrdiff_t start_byte, ptrdiff_t old_end_byte,
+ ptrdiff_t new_end_byte)
{
- ptrdiff_t new_end_byte = CHAR_TO_BYTE (charpos + lenins);
Lisp_Object parser_list = Fsymbol_value (Qtree_sitter_parser_list);
+ TSPoint dummy_point = {0, 0};
+ TSInputEdit edit = {start_byte, old_end_byte, new_end_byte,
+ dummy_point, dummy_point, dummy_point};
while (!NILP (parser_list))
{
Lisp_Object lisp_parser = Fcar (parser_list);
TSTree *tree = XTS_PARSER (lisp_parser)->tree;
- XTS_PARSER (lisp_parser)->edit.new_end_byte = new_end_byte;
if (tree != NULL)
- ts_tree_edit (tree, &XTS_PARSER (lisp_parser)->edit);
+ ts_tree_edit (tree, &edit);
+ XTS_PARSER (lisp_parser)->need_reparse = true;
parser_list = Fcdr (parser_list);
}
+
}
/* Parse the buffer. We don't parse until we have to. When we have
@@ -82,11 +85,15 @@ ts_after_change (ptrdiff_t charpos, ptrdiff_t lendel, ptrdiff_t lenins)
void
ts_ensure_parsed (Lisp_Object parser)
{
+ if (!XTS_PARSER (parser)->need_reparse)
+ return;
TSParser *ts_parser = XTS_PARSER (parser)->parser;
TSTree *tree = XTS_PARSER(parser)->tree;
TSInput input = XTS_PARSER (parser)->input;
TSTree *new_tree = ts_parser_parse(ts_parser, tree, input);
+ ts_tree_delete (tree);
XTS_PARSER (parser)->tree = new_tree;
+ XTS_PARSER (parser)->need_reparse = false;
}
/* This is the read function provided to tree-sitter to read from a
@@ -96,33 +103,30 @@ ts_ensure_parsed (Lisp_Object parser)
ts_read_buffer (void *buffer, uint32_t byte_index,
TSPoint position, uint32_t *bytes_read)
{
- if (! BUFFER_LIVE_P ((struct buffer *) buffer))
+ if (!BUFFER_LIVE_P ((struct buffer *) buffer))
error ("BUFFER is not live");
ptrdiff_t byte_pos = byte_index + 1;
- // FIXME: Add some boundary checks?
- /* I believe we can get away with only setting current-buffer
- and not actually switching to it, like what we did in
- 'make_gap_1'. */
- struct buffer *old_buffer = current_buffer;
- current_buffer = (struct buffer *) buffer;
-
- /* Read one character. */
+ /* Read one character. Tree-sitter wants us to set bytes_read to 0
+ if it reads to the end of buffer. It doesn't say what it wants
+ for the return value in that case, so we just give it an empty
+ string. */
char *beg;
int len;
- if (byte_pos >= Z_BYTE)
+ // TODO BUF_ZV_BYTE?
+ if (byte_pos >= BUF_Z_BYTE ((struct buffer *) buffer))
{
beg = "";
len = 0;
}
else
{
- beg = (char *) BYTE_POS_ADDR (byte_pos);
+ beg = (char *) BUF_BYTE_ADDRESS (buffer, byte_pos);
len = next_char_len(byte_pos);
}
*bytes_read = (uint32_t) len;
- current_buffer = old_buffer;
+
return beg;
}
@@ -137,9 +141,7 @@ make_ts_parser (struct buffer *buffer, TSParser *parser, TSTree *tree)
lisp_parser->tree = tree;
TSInput input = {buffer, ts_read_buffer, TSInputEncodingUTF8};
lisp_parser->input = input;
- TSPoint dummy_point = {0, 0};
- TSInputEdit edit = {0, 0, 0, dummy_point, dummy_point, dummy_point};
- lisp_parser->edit = edit;
+ lisp_parser->need_reparse = true;
return make_lisp_ptr (lisp_parser, Lisp_Vectorlike);
}
@@ -192,6 +194,7 @@ DEFUN ("tree-sitter-parser-root-node",
doc: /* Return the root node of PARSER. */)
(Lisp_Object parser)
{
+ CHECK_TS_PARSER (parser);
ts_ensure_parsed(parser);
TSNode root_node = ts_tree_root_node (XTS_PARSER (parser)->tree);
return make_ts_node (parser, root_node);
@@ -229,11 +232,29 @@ DEFUN ("tree-sitter-parse", Ftree_sitter_parse, Stree_sitter_parse,
return lisp_node;
}
+/* Below this point are uninteresting mechanical translations of
+ tree-sitter API. */
+
+/* Node functions. */
+
+DEFUN ("tree-sitter-node-type",
+ Ftree_sitter_node_type, Stree_sitter_node_type, 1, 1, 0,
+ doc: /* Return the NODE's type as a symbol. */)
+ (Lisp_Object node)
+{
+ CHECK_TS_NODE (node);
+ TSNode ts_node = XTS_NODE (node)->node;
+ const char *type = ts_node_type(ts_node);
+ return intern_c_string (type);
+}
+
+
DEFUN ("tree-sitter-node-string",
Ftree_sitter_node_string, Stree_sitter_node_string, 1, 1, 0,
doc: /* Return the string representation of NODE. */)
(Lisp_Object node)
{
+ CHECK_TS_NODE (node);
TSNode ts_node = XTS_NODE (node)->node;
char *string = ts_node_string(ts_node);
return make_string(string, strlen (string));
@@ -242,29 +263,125 @@ DEFUN ("tree-sitter-node-string",
DEFUN ("tree-sitter-node-parent",
Ftree_sitter_node_parent, Stree_sitter_node_parent, 1, 1, 0,
doc: /* Return the immediate parent of NODE.
-Return nil if we couldn't find any. */)
+Return nil if there isn't any. */)
(Lisp_Object node)
{
+ CHECK_TS_NODE (node);
TSNode ts_node = XTS_NODE (node)->node;
- TSNode parent = ts_node_parent(ts_node);
+ TSNode parent = ts_node_parent (ts_node);
if (ts_node_is_null(parent))
return Qnil;
- return make_ts_node(XTS_NODE (node)->parser, parent);
+ return make_ts_node (XTS_NODE (node)->parser, parent);
}
DEFUN ("tree-sitter-node-child",
- Ftree_sitter_node_child, Stree_sitter_node_child, 2, 2, 0,
+ Ftree_sitter_node_child, Stree_sitter_node_child, 2, 3, 0,
doc: /* Return the Nth child of NODE.
-Return nil if we couldn't find any. */)
+Return nil if there isn't any. If NAMED is non-nil, look for named
+child only. NAMED defaults to nil. */)
+ (Lisp_Object node, Lisp_Object n, Lisp_Object named)
+{
+ CHECK_TS_NODE (node);
+ CHECK_INTEGER (n);
+ EMACS_INT idx = XFIXNUM (n);
+ TSNode ts_node = XTS_NODE (node)->node;
+ TSNode child;
+ if (NILP (named))
+ child = ts_node_child (ts_node, (uint32_t) idx);
+ else
+ child = ts_node_named_child (ts_node, (uint32_t) idx);
+
+ if (ts_node_is_null(child))
+ return Qnil;
+
+ return make_ts_node(XTS_NODE (node)->parser, child);
+}
+
+DEFUN ("tree-sitter-node-check",
+ Ftree_sitter_node_check, Stree_sitter_node_check, 2, 2, 0,
+ doc: /* Return non-nil if NODE is in condition COND, nil otherwise.
+
+COND could be 'named, 'missing, 'extra, 'has-error. Named nodes
+correspond to named rules in the grammar, whereas "anonymous" nodes
+correspond to string literals in the grammar.
+
+Missing nodes are inserted by the parser in order to recover from
+certain kinds of syntax errors, i.e., should be there but not there.
+
+Extra nodes represent things like comments, which are not required the
+grammar, but can appear anywhere.
+
+A node "has error" if itself is a syntax error or contains any syntax
+errors. */)
+ (Lisp_Object node, Lisp_Object cond)
+{
+ CHECK_TS_NODE (node);
+ CHECK_SYMBOL (cond);
+ TSNode ts_node = XTS_NODE (node)->node;
+ bool result;
+ if (EQ (cond, Qnamed))
+ result = ts_node_is_named (ts_node);
+ else if (EQ (cond, Qmissing))
+ result = ts_node_is_missing (ts_node);
+ else if (EQ (cond, Qextra))
+ result = ts_node_is_extra (ts_node);
+ else if (EQ (cond, Qhas_error))
+ result = ts_node_has_error (ts_node);
+ else
+ signal_error ("Expecting one of four symbols, see docstring", cond);
+ return result ? Qt : Qnil;
+}
+
+DEFUN ("tree-sitter-node-field-name-for-child",
+ Ftree_sitter_node_field_name_for_child,
+ Stree_sitter_node_field_name_for_child, 2, 2, 0,
+ doc: /* Return the field name of the Nth child of NODE.
+Return nil if there isn't any child or no field is found. */)
(Lisp_Object node, Lisp_Object n)
{
CHECK_INTEGER (n);
EMACS_INT idx = XFIXNUM (n);
TSNode ts_node = XTS_NODE (node)->node;
- // FIXME: Is this cast ok?
- TSNode child = ts_node_child(ts_node, (uint32_t) idx);
+ const char *name
+ = ts_node_field_name_for_child (ts_node, (uint32_t) idx);
+
+ if (name == NULL)
+ return Qnil;
+
+ return make_string (name, strlen (name));
+}
+
+DEFUN ("tree-sitter-node-child-count",
+ Ftree_sitter_node_child_count,
+ Stree_sitter_node_child_count, 1, 2, 0,
+ doc: /* Return the number of children of NODE.
+If NAMED is non-nil, count named child only. NAMED defaults to
+nil. */)
+ (Lisp_Object node, Lisp_Object named)
+{
+ TSNode ts_node = XTS_NODE (node)->node;
+ uint32_t count;
+ if (NILP (named))
+ count = ts_node_child_count (ts_node);
+ else
+ count = ts_node_named_child_count (ts_node);
+ return make_fixnum (count);
+}
+
+DEFUN ("tree-sitter-node-child-by-field-name",
+ Ftree_sitter_node_child_by_field_name,
+ Stree_sitter_node_child_by_field_name, 2, 2, 0,
+ doc: /* Return the child of NODE with field name NAME.
+Return nil if there isn't any. */)
+ (Lisp_Object node, Lisp_Object name)
+{
+ CHECK_STRING (name);
+ char *name_str = SSDATA (name);
+ TSNode ts_node = XTS_NODE (node)->node;
+ TSNode child
+ = ts_node_child_by_field_name (ts_node, name_str, strlen (name_str));
if (ts_node_is_null(child))
return Qnil;
@@ -272,10 +389,62 @@ DEFUN ("tree-sitter-node-child",
return make_ts_node(XTS_NODE (node)->parser, child);
}
+DEFUN ("tree-sitter-node-next-sibling",
+ Ftree_sitter_node_next_sibling,
+ Stree_sitter_node_next_sibling, 1, 2, 0,
+ doc: /* Return the next sibling of NODE.
+Return nil if there isn't any. If NAMED is non-nil, look for named
+child only. NAMED defaults to nil. */)
+ (Lisp_Object node, Lisp_Object named)
+{
+ TSNode ts_node = XTS_NODE (node)->node;
+ TSNode sibling;
+ if (NILP (named))
+ sibling = ts_node_next_sibling (ts_node);
+ else
+ sibling = ts_node_next_named_sibling (ts_node);
+
+ if (ts_node_is_null(sibling))
+ return Qnil;
+
+ return make_ts_node(XTS_NODE (node)->parser, sibling);
+}
+
+DEFUN ("tree-sitter-node-prev-sibling",
+ Ftree_sitter_node_prev_sibling,
+ Stree_sitter_node_prev_sibling, 1, 2, 0,
+ doc: /* Return the previous sibling of NODE.
+Return nil if there isn't any. If NAMED is non-nil, look for named
+child only. NAMED defaults to nil. */)
+ (Lisp_Object node, Lisp_Object named)
+{
+ TSNode ts_node = XTS_NODE (node)->node;
+ TSNode sibling;
+
+ if (NILP (named))
+ sibling = ts_node_prev_sibling (ts_node);
+ else
+ sibling = ts_node_prev_named_sibling (ts_node);
+
+ if (ts_node_is_null(sibling))
+ return Qnil;
+
+ return make_ts_node(XTS_NODE (node)->parser, sibling);
+}
+
+/* Query functions */
+
/* Initialize the tree-sitter routines. */
void
syms_of_tree_sitter (void)
{
+ DEFSYM (Qtree_sitter_parser_p, "tree-sitter-parser-p");
+ DEFSYM (Qtree_sitter_node_p, "tree-sitter-node-p");
+ DEFSYM (Qnamed, "named");
+ DEFSYM (Qmissing, "missing");
+ DEFSYM (Qextra, "extra");
+ DEFSYM (Qhas_error, "has-error");
+
DEFSYM (Qtree_sitter_parser_list, "tree-sitter-parser-list");
DEFVAR_LISP ("ts-parser-list", Vtree_sitter_parser_list,
doc: /* A list of tree-sitter parsers.
@@ -284,11 +453,20 @@ syms_of_tree_sitter (void)
Vtree_sitter_parser_list = Qnil;
Fmake_variable_buffer_local (Qtree_sitter_parser_list);
-
+ defsubr (&Stree_sitter_parser_p);
+ defsubr (&Stree_sitter_node_p);
defsubr (&Stree_sitter_create_parser);
defsubr (&Stree_sitter_parser_root_node);
defsubr (&Stree_sitter_parse);
+
+ defsubr (&Stree_sitter_node_type);
defsubr (&Stree_sitter_node_string);
defsubr (&Stree_sitter_node_parent);
defsubr (&Stree_sitter_node_child);
+ defsubr (&Stree_sitter_node_check);
+ defsubr (&Stree_sitter_node_field_name_for_child);
+ defsubr (&Stree_sitter_node_child_count);
+ defsubr (&Stree_sitter_node_child_by_field_name);
+ defsubr (&Stree_sitter_node_next_sibling);
+ defsubr (&Stree_sitter_node_prev_sibling);
}
diff --git a/src/tree_sitter.h b/src/tree_sitter.h
index 0606f336cc..a7e2a2d670 100644
--- a/src/tree_sitter.h
+++ b/src/tree_sitter.h
@@ -37,7 +37,7 @@ #define EMACS_TREE_SITTER_H
TSParser *parser;
TSTree *tree;
TSInput input;
- TSInputEdit edit;
+ bool need_reparse;
};
/* A wrapper around a tree-sitter node. */
@@ -78,11 +78,21 @@ XTS_NODE (Lisp_Object a)
return XUNTAG (a, Lisp_Vectorlike, struct Lisp_TS_Node);
}
-void
-ts_before_change (ptrdiff_t charpos, ptrdiff_t lendel);
+INLINE void
+CHECK_TS_PARSER (Lisp_Object parser)
+{
+ CHECK_TYPE (TS_PARSERP (parser), Qtree_sitter_parser_p, parser);
+}
+
+INLINE void
+CHECK_TS_NODE (Lisp_Object node)
+{
+ CHECK_TYPE (TS_NODEP (node), Qtree_sitter_node_p, node);
+}
void
-ts_after_change (ptrdiff_t charpos, ptrdiff_t lendel, ptrdiff_t lenins);
+ts_record_change (ptrdiff_t start_byte, ptrdiff_t old_end_byte,
+ ptrdiff_t new_end_byte);
Lisp_Object
make_ts_parser (struct buffer *buffer, TSParser *parser, TSTree *tree);
diff --git a/test/src/tree-sitter-tests.el b/test/src/tree-sitter-tests.el
new file mode 100644
index 0000000000..cb1c464d3a
--- /dev/null
+++ b/test/src/tree-sitter-tests.el
@@ -0,0 +1,106 @@
+;;; tree-sitter-tests.el --- tests for src/tree-sitter.c -*- lexical-binding: t; -*-
+
+;; Copyright (C) 2021 Free Software Foundation, Inc.
+
+;; This file is part of GNU Emacs.
+
+;; GNU Emacs 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.
+
+;; GNU Emacs 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 GNU Emacs. If not, see <https://www.gnu.org/licenses/>.
+
+;;; Code:
+
+(require 'ert)
+(require 'tree-sitter-json)
+
+(ert-deftest tree-sitter-basic-parsing ()
+ "Test basic parsing routines."
+ (with-temp-buffer
+ (let ((parser (tree-sitter-create-parser
+ (current-buffer) (tree-sitter-json))))
+ (should
+ (eq parser (car tree-sitter-parser-list)))
+ (should
+ (equal (tree-sitter-node-string
+ (tree-sitter-parser-root-node parser))
+ "(ERROR)"))
+
+ (insert "[1,2,3]")
+ (should
+ (equal (tree-sitter-node-string
+ (tree-sitter-parser-root-node parser))
+ "(document (array (number) (number) (number)))"))
+
+ (goto-char (point-min))
+ (forward-char 3)
+ (insert "{\"name\": \"Bob\"},")
+ (should
+ (equal
+ (tree-sitter-node-string
+ (tree-sitter-parser-root-node parser))
+ "(document (array (number) (object (pair key: (string (string_content)) value: (string (string_content)))) (number) (number)))")))))
+
+(ert-deftest tree-sitter-node-api ()
+ "Tests for node API."
+ (with-temp-buffer
+ (insert "[1,2,{\"name\": \"Bob\"},3]")
+ (let (parser root-node doc-node object-node pair-node)
+ (setq parser (tree-sitter-create-parser
+ (current-buffer) (tree-sitter-json)))
+ (setq root-node (tree-sitter-parser-root-node
+ parser))
+ ;; `tree-sitter-node-type'.
+ (should (eq 'document (tree-sitter-node-type root-node)))
+ ;; `tree-sitter-node-check'.
+ (should (eq t (tree-sitter-node-check root-node 'named)))
+ (should (eq nil (tree-sitter-node-check root-node 'missing)))
+ (should (eq nil (tree-sitter-node-check root-node 'extra)))
+ (should (eq nil (tree-sitter-node-check root-node 'has-error)))
+ ;; `tree-sitter-node-child'.
+ (setq doc-node (tree-sitter-node-child root-node 0))
+ (should (eq 'array (tree-sitter-node-type doc-node)))
+ (should (equal (tree-sitter-node-string doc-node)
+ "(array (number) (number) (object (pair key: (string (string_content)) value: (string (string_content)))) (number))"))
+ ;; `tree-sitter-node-child-count'.
+ (should (eql 9 (tree-sitter-node-child-count doc-node)))
+ (should (eql 4 (tree-sitter-node-child-count doc-node t)))
+ ;; `tree-sitter-node-field-name-for-child'.
+ (setq object-node (tree-sitter-node-child doc-node 2 t))
+ (setq pair-node (tree-sitter-node-child object-node 0 t))
+ (should (eq 'object (tree-sitter-node-type object-node)))
+ (should (eq 'pair (tree-sitter-node-type pair-node)))
+ (should (equal "key"
+ (tree-sitter-node-field-name-for-child
+ pair-node 0)))
+ ;; `tree-sitter-node-child-by-field-name'.
+ (should (equal "(string (string_content))"
+ (tree-sitter-node-string
+ (tree-sitter-node-child-by-field-name
+ pair-node "key"))))
+ ;; `tree-sitter-node-next-sibling'.
+ (should (equal "(number)"
+ (tree-sitter-node-string
+ (tree-sitter-node-next-sibling object-node t))))
+ (should (equal "(\",\")"
+ (tree-sitter-node-string
+ (tree-sitter-node-next-sibling object-node))))
+ ;; `tree-sitter-node-prev-sibling'.
+ (should (equal "(number)"
+ (tree-sitter-node-string
+ (tree-sitter-node-prev-sibling object-node t))))
+ (should (equal "(\",\")"
+ (tree-sitter-node-string
+ (tree-sitter-node-prev-sibling object-node))))
+ )))
+
+(provide 'tree-sitter-tests)
+;;; tree-sitter-tests.el ends here
--
2.24.3 (Apple Git-128)
next prev parent reply other threads:[~2021-07-22 3:10 UTC|newest]
Thread overview: 370+ messages / expand[flat|nested] mbox.gz Atom feed top
2021-07-14 17:37 How to add pseudo vector types Yuan Fu
2021-07-14 17:44 ` Eli Zaretskii
2021-07-14 17:47 ` Stefan Monnier
2021-07-14 23:48 ` Yuan Fu
2021-07-15 0:26 ` Yuan Fu
2021-07-15 2:48 ` Yuan Fu
2021-07-15 6:39 ` Eli Zaretskii
2021-07-15 13:37 ` Fu Yuan
2021-07-15 14:18 ` Eli Zaretskii
2021-07-15 15:17 ` Yuan Fu
2021-07-15 15:50 ` Eli Zaretskii
2021-07-15 16:19 ` Yuan Fu
2021-07-15 16:26 ` Yuan Fu
2021-07-15 16:50 ` Eli Zaretskii
2021-07-15 16:48 ` Eli Zaretskii
2021-07-15 18:23 ` Yuan Fu
2021-07-16 7:30 ` Eli Zaretskii
2021-07-16 14:27 ` Yuan Fu
2021-07-16 14:33 ` Stefan Monnier
2021-07-16 14:53 ` Yuan Fu
2021-07-16 15:27 ` Eli Zaretskii
2021-07-16 15:51 ` Yuan Fu
2021-07-17 2:05 ` Yuan Fu
2021-07-17 2:23 ` Clément Pit-Claudel
2021-07-17 3:12 ` Yuan Fu
2021-07-17 7:18 ` Eli Zaretskii
2021-07-17 7:16 ` Eli Zaretskii
2021-07-20 20:36 ` Clément Pit-Claudel
2021-07-21 11:26 ` Eli Zaretskii
2021-07-21 13:38 ` Clément Pit-Claudel
2021-07-21 13:51 ` Eli Zaretskii
2021-07-22 4:59 ` Clément Pit-Claudel
2021-07-22 6:38 ` Eli Zaretskii
2021-07-21 16:29 ` Stephen Leake
2021-07-21 16:54 ` Clément Pit-Claudel
2021-07-21 19:43 ` Eli Zaretskii
2021-07-24 2:57 ` Stephen Leake
2021-07-24 3:39 ` Óscar Fuentes
2021-07-24 7:34 ` Eli Zaretskii
2021-07-25 16:49 ` Stephen Leake
2021-07-24 7:06 ` Eli Zaretskii
2021-07-25 17:48 ` Stephen Leake
2021-07-24 3:55 ` Clément Pit-Claudel
2021-07-21 21:54 ` Stephen Leake
2021-07-22 4:40 ` Clément Pit-Claudel
2021-07-17 17:30 ` Stefan Monnier
2021-07-17 17:54 ` Eli Zaretskii
2021-07-24 14:08 ` Stefan Monnier
2021-07-24 14:32 ` Eli Zaretskii
2021-07-24 15:10 ` Stefan Monnier
2021-07-24 15:51 ` Eli Zaretskii
2021-07-19 15:16 ` Yuan Fu
2021-07-22 3:10 ` Yuan Fu [this message]
2021-07-22 8:23 ` Eli Zaretskii
2021-07-22 13:47 ` Yuan Fu
2021-07-22 14:11 ` Óscar Fuentes
2021-07-22 17:09 ` Eli Zaretskii
2021-07-22 19:29 ` Óscar Fuentes
2021-07-23 5:21 ` Eli Zaretskii
2021-07-24 9:38 ` Stephen Leake
2021-07-22 17:00 ` Eli Zaretskii
2021-07-22 17:47 ` Yuan Fu
2021-07-22 19:05 ` Eli Zaretskii
2021-07-23 13:25 ` Yuan Fu
2021-07-23 19:10 ` Eli Zaretskii
2021-07-23 20:01 ` Perry E. Metzger
2021-07-24 5:52 ` Eli Zaretskii
2021-07-23 20:22 ` Yuan Fu
2021-07-24 6:00 ` Eli Zaretskii
2021-07-25 18:01 ` Stephen Leake
2021-07-25 19:09 ` Eli Zaretskii
2021-07-26 5:10 ` Stephen Leake
2021-07-26 12:56 ` Eli Zaretskii
2021-07-24 15:04 ` Yuan Fu
2021-07-24 15:48 ` Eli Zaretskii
2021-07-24 17:14 ` Yuan Fu
2021-07-24 17:20 ` Eli Zaretskii
2021-07-24 17:40 ` Yuan Fu
2021-07-24 17:46 ` Eli Zaretskii
2021-07-24 18:06 ` Yuan Fu
2021-07-24 18:21 ` Eli Zaretskii
2021-07-24 18:55 ` Stefan Monnier
2021-07-25 18:44 ` Stephen Leake
2021-07-26 14:38 ` Perry E. Metzger
2021-07-24 16:14 ` Eli Zaretskii
2021-07-24 17:32 ` Yuan Fu
2021-07-24 17:42 ` Eli Zaretskii
2021-07-23 14:07 ` Stefan Monnier
2021-07-23 14:45 ` Yuan Fu
2021-07-23 19:13 ` Eli Zaretskii
2021-07-23 20:28 ` Stefan Monnier
2021-07-24 6:02 ` Eli Zaretskii
2021-07-24 14:19 ` Stefan Monnier
2021-07-24 9:42 ` Stephen Leake
2021-07-24 11:22 ` Eli Zaretskii
2021-07-25 18:21 ` Stephen Leake
2021-07-25 19:03 ` Eli Zaretskii
2021-07-26 16:40 ` Yuan Fu
2021-07-26 16:49 ` Eli Zaretskii
2021-07-26 17:09 ` Yuan Fu
2021-07-26 18:55 ` Eli Zaretskii
2021-07-26 19:06 ` Yuan Fu
2021-07-26 19:19 ` Perry E. Metzger
2021-07-26 19:31 ` Eli Zaretskii
2021-07-26 19:20 ` Eli Zaretskii
2021-07-26 19:45 ` Yuan Fu
2021-07-26 19:57 ` Dmitry Gutov
2021-07-27 6:13 ` Stephen Leake
2021-07-27 14:56 ` Yuan Fu
2021-07-28 3:40 ` Stephen Leake
2021-07-28 16:36 ` Yuan Fu
2021-07-28 16:41 ` Eli Zaretskii
2021-07-29 22:58 ` Stephen Leake
2021-07-30 6:00 ` Eli Zaretskii
2021-07-28 16:43 ` Eli Zaretskii
2021-07-28 17:47 ` Yuan Fu
2021-07-28 17:54 ` Eli Zaretskii
2021-07-28 18:46 ` Yuan Fu
2021-07-28 19:00 ` Eli Zaretskii
2021-07-29 14:35 ` Yuan Fu
2021-07-29 15:28 ` Eli Zaretskii
2021-07-29 15:57 ` Yuan Fu
2021-07-29 16:21 ` Eli Zaretskii
2021-07-29 16:59 ` Yuan Fu
2021-07-29 17:38 ` Eli Zaretskii
2021-07-29 17:55 ` Yuan Fu
2021-07-29 18:37 ` Eli Zaretskii
2021-07-29 18:57 ` Yuan Fu
2021-07-30 6:47 ` Eli Zaretskii
2021-07-30 14:17 ` Yuan Fu
2021-08-03 10:24 ` Fu Yuan
2021-08-03 11:42 ` Eli Zaretskii
2021-08-03 11:53 ` Fu Yuan
2021-08-03 12:21 ` Eli Zaretskii
2021-08-03 12:50 ` Fu Yuan
2021-08-03 13:03 ` Eli Zaretskii
2021-08-03 13:08 ` Fu Yuan
2021-08-03 11:47 ` Eli Zaretskii
2021-08-03 12:00 ` Fu Yuan
2021-08-03 12:24 ` Eli Zaretskii
2021-08-03 13:00 ` Fu Yuan
2021-08-03 13:28 ` Stefan Monnier
2021-08-03 13:34 ` Eli Zaretskii
2021-08-06 3:22 ` Yuan Fu
2021-08-06 6:37 ` Eli Zaretskii
2021-08-07 5:31 ` Tree-sitter api (Was: Re: How to add pseudo vector types) Fu Yuan
2021-08-07 6:26 ` Eli Zaretskii
2021-08-07 15:47 ` Tree-sitter api Stefan Monnier
2021-08-07 18:40 ` Theodor Thornhill
2021-08-07 19:53 ` Stefan Monnier
2021-08-17 6:18 ` Yuan Fu
2021-08-18 18:27 ` Stephen Leake
2021-08-18 21:30 ` Yuan Fu
2021-08-20 0:12 ` [SPAM UNSURE] " Stephen Leake
2021-08-23 6:51 ` Yuan Fu
2021-08-24 14:59 ` [SPAM UNSURE] " Stephen Leake
2021-08-27 5:18 ` [SPAM UNSURE] " Yuan Fu
2021-08-31 0:48 ` Stephen Leake
2021-08-24 22:51 ` Stefan Monnier
2021-08-22 2:43 ` Yuan Fu
2021-08-22 3:46 ` Yuan Fu
2021-08-22 6:16 ` Eli Zaretskii
2021-08-22 6:15 ` Eli Zaretskii
2021-08-25 0:21 ` Stefan Monnier
2021-08-27 5:45 ` Yuan Fu
2021-09-03 19:16 ` Theodor Thornhill
[not found] ` <AF64EB2C-CCEC-4C98-8FE3-37697BEC9098@gmail.com>
2021-09-04 12:49 ` Tuấn-Anh Nguyễn
2021-09-04 13:04 ` Eli Zaretskii
2021-09-04 14:49 ` Tuấn-Anh Nguyễn
2021-09-04 15:00 ` Eli Zaretskii
2021-09-05 16:34 ` Tuấn-Anh Nguyễn
2021-09-05 16:45 ` Eli Zaretskii
2021-09-04 15:31 ` Yuan Fu
2021-09-05 16:45 ` Tuấn-Anh Nguyễn
2021-09-05 20:19 ` Yuan Fu
2021-09-06 0:03 ` Tuấn-Anh Nguyễn
2021-09-06 0:23 ` Yuan Fu
2021-09-06 5:33 ` Eli Zaretskii
2021-09-07 15:38 ` Tuấn-Anh Nguyễn
2021-09-07 16:16 ` Eli Zaretskii
2021-09-08 3:06 ` Yuan Fu
2021-09-10 2:06 ` Yuan Fu
2021-09-10 6:32 ` Eli Zaretskii
2021-09-10 19:57 ` Yuan Fu
2021-09-11 3:41 ` Tuấn-Anh Nguyễn
2021-09-11 4:11 ` Yuan Fu
2021-09-11 7:23 ` Tuấn-Anh Nguyễn
2021-09-11 19:02 ` Yuan Fu
2021-09-11 5:51 ` Eli Zaretskii
2021-09-11 19:00 ` Yuan Fu
2021-09-11 19:14 ` Eli Zaretskii
2021-09-11 19:17 ` Eli Zaretskii
2021-09-11 20:29 ` Yuan Fu
2021-09-12 5:39 ` Eli Zaretskii
2021-09-13 4:15 ` Yuan Fu
2021-09-13 11:47 ` Eli Zaretskii
2021-09-13 18:01 ` Yuan Fu
2021-09-13 18:07 ` Eli Zaretskii
2021-09-13 18:29 ` Yuan Fu
2021-09-13 18:37 ` Eli Zaretskii
2021-09-14 0:13 ` Yuan Fu
2021-09-14 2:29 ` Eli Zaretskii
2021-09-14 4:27 ` Yuan Fu
2021-09-14 11:29 ` Eli Zaretskii
2021-09-15 0:50 ` Yuan Fu
2021-09-15 6:15 ` Eli Zaretskii
2021-09-15 15:56 ` Yuan Fu
2021-09-15 16:02 ` Eli Zaretskii
2021-09-15 18:19 ` Stefan Monnier
2021-09-15 18:48 ` Eli Zaretskii
2021-09-16 21:46 ` Yuan Fu
2021-09-17 6:06 ` Eli Zaretskii
2021-09-17 6:56 ` Yuan Fu
2021-09-17 7:38 ` Eli Zaretskii
2021-09-17 20:30 ` Yuan Fu
2021-09-18 2:22 ` Tuấn-Anh Nguyễn
2021-09-18 6:38 ` Yuan Fu
2021-09-18 12:33 ` Stephen Leake
2021-09-20 16:48 ` Yuan Fu
2021-09-20 18:48 ` Eli Zaretskii
2021-09-20 19:09 ` John Yates
2021-09-21 22:20 ` Yuan Fu
2021-09-27 4:42 ` Yuan Fu
2021-09-27 5:37 ` Eli Zaretskii
2021-09-27 19:17 ` Stefan Monnier
2021-09-28 5:33 ` Yuan Fu
2021-09-28 7:02 ` Eli Zaretskii
2021-09-28 16:10 ` Yuan Fu
2021-09-28 16:28 ` Eli Zaretskii
2021-12-13 6:54 ` Yuan Fu
2021-12-13 12:56 ` Eli Zaretskii
2021-12-14 7:19 ` Yuan Fu
2021-12-17 0:14 ` Yuan Fu
2021-12-17 7:15 ` Eli Zaretskii
2021-12-18 14:45 ` Philipp
2021-12-18 14:57 ` Eli Zaretskii
2021-12-19 2:51 ` Yuan Fu
2021-12-19 7:11 ` Eli Zaretskii
2021-12-19 7:52 ` Yuan Fu
2021-12-24 10:04 ` Yoav Marco
2021-12-24 10:21 ` Yoav Marco
2021-12-25 8:31 ` Yuan Fu
2021-12-25 10:13 ` Eli Zaretskii
2021-12-26 9:50 ` Yuan Fu
2021-12-26 10:23 ` Eli Zaretskii
2021-12-30 0:59 ` Yuan Fu
2021-12-30 6:35 ` Eli Zaretskii
2022-01-04 18:31 ` Yuan Fu
2022-03-13 6:22 ` Yuan Fu
2022-03-13 6:25 ` Yuan Fu
2022-03-13 7:13 ` Po Lu
2022-03-14 0:23 ` Yuan Fu
2022-03-14 1:10 ` Po Lu
2022-03-14 3:31 ` Eli Zaretskii
2022-03-14 3:43 ` Yuan Fu
2022-03-29 16:40 ` Eli Zaretskii
2022-03-30 0:35 ` Po Lu
2022-03-30 0:49 ` Yuan Fu
2022-03-30 0:51 ` Yuan Fu
2022-03-30 2:13 ` Po Lu
2022-03-30 3:01 ` Yuan Fu
2022-03-30 3:10 ` Vitaly Ankh
2022-03-30 3:24 ` Yuan Fu
2022-03-30 3:39 ` Po Lu
2022-03-30 4:29 ` Yuan Fu
2022-03-30 5:19 ` Phil Sainty
2022-03-30 5:39 ` Phil Sainty
2022-03-30 13:46 ` João Távora
2022-03-30 2:31 ` Eli Zaretskii
2022-03-30 2:59 ` Yuan Fu
2022-03-30 9:04 ` Lars Ingebrigtsen
2022-03-30 11:48 ` Daniel Martín
2022-03-30 15:00 ` [External] : " Drew Adams
2022-03-31 4:27 ` Richard Stallman
2022-03-31 5:36 ` Eli Zaretskii
2022-03-31 11:13 ` Lars Ingebrigtsen
2022-03-31 12:46 ` John Yates
2022-03-31 17:37 ` Phil Sainty
2022-04-01 1:56 ` Po Lu
2022-04-01 6:36 ` Eli Zaretskii
2022-04-01 7:56 ` Po Lu
2022-04-01 10:45 ` Eli Zaretskii
2022-03-31 17:58 ` Stefan Monnier
2022-04-04 10:29 ` Jostein Kjønigsen
2022-03-31 16:23 ` [External] : " Drew Adams
2022-03-31 19:33 ` Filipp Gunbin
2022-03-31 16:35 ` Yuan Fu
2022-03-31 23:00 ` Yuan Fu
2022-03-31 23:53 ` Yuan Fu
2022-04-01 6:20 ` Eli Zaretskii
2022-04-01 16:48 ` Yuan Fu
2022-04-01 17:59 ` Eli Zaretskii
2022-04-02 6:26 ` Yuan Fu
2022-04-04 7:38 ` Robert Pluim
2022-04-04 20:41 ` Yuan Fu
2022-04-20 20:14 ` Theodor Thornhill
2022-04-21 1:36 ` Yuan Fu
2022-04-21 5:48 ` Eli Zaretskii
2022-04-21 11:37 ` Lars Ingebrigtsen
2022-04-21 12:10 ` Theodor Thornhill
2022-04-22 2:54 ` Yuan Fu
2022-04-22 4:58 ` Theodor Thornhill
2022-04-22 7:08 ` Yuan Fu
2022-04-22 8:02 ` Theodor Thornhill
2022-04-22 11:41 ` Theodor Thornhill
2021-12-18 13:39 ` Daniel Martín
2021-12-19 2:48 ` Yuan Fu
2021-09-17 12:11 ` Tuấn-Anh Nguyễn
2021-09-17 13:14 ` Stefan Monnier
2021-09-17 13:39 ` Tuấn-Anh Nguyễn
2021-09-17 17:18 ` Stefan Monnier
2021-09-18 2:16 ` Tuấn-Anh Nguyễn
2021-09-17 12:23 ` Stefan Monnier
2021-09-17 13:03 ` Tuấn-Anh Nguyễn
2021-09-04 15:14 ` Tuấn-Anh Nguyễn
2021-09-04 15:33 ` Eli Zaretskii
2021-09-05 16:48 ` Tuấn-Anh Nguyễn
2021-09-04 15:39 ` Yuan Fu
2021-09-05 21:15 ` Theodor Thornhill
2021-09-05 23:58 ` Yuan Fu
2021-08-08 22:56 ` Yuan Fu
2021-08-08 23:24 ` Stefan Monnier
2021-08-09 0:06 ` Yuan Fu
2021-07-29 23:06 ` How to add pseudo vector types Stephen Leake
2021-07-30 0:35 ` Richard Stallman
2021-07-30 0:46 ` Alexandre Garreau
2021-07-30 6:35 ` Eli Zaretskii
2021-07-29 23:01 ` Stephen Leake
2021-07-26 18:32 ` chad
2021-07-26 18:44 ` Perry E. Metzger
2021-07-26 19:13 ` Eli Zaretskii
2021-07-26 19:09 ` Eli Zaretskii
2021-07-26 19:48 ` chad
2021-07-26 20:05 ` Óscar Fuentes
2021-07-26 21:30 ` Clément Pit-Claudel
2021-07-26 21:46 ` Óscar Fuentes
2021-07-27 14:02 ` Eli Zaretskii
2021-07-27 13:59 ` Eli Zaretskii
2021-07-26 23:40 ` Ergus
2021-07-27 14:49 ` Yuan Fu
2021-07-27 16:50 ` Ergus
2021-07-27 16:59 ` Eli Zaretskii
2021-07-28 3:45 ` Stephen Leake
2021-07-24 9:33 ` Stephen Leake
2021-07-24 22:54 ` Dmitry Gutov
2021-07-20 16:32 ` Stephen Leake
2021-07-20 16:48 ` Eli Zaretskii
2021-07-20 17:38 ` Stefan Monnier
2021-07-20 17:36 ` Stefan Monnier
2021-07-20 18:05 ` Clément Pit-Claudel
2021-07-21 16:02 ` Stephen Leake
2021-07-21 17:16 ` Stefan Monnier
2021-07-20 18:04 ` Clément Pit-Claudel
2021-07-20 18:24 ` Eli Zaretskii
2021-07-21 16:54 ` [SPAM UNSURE] " Stephen Leake
2021-07-21 17:12 ` Clément Pit-Claudel
2021-07-21 19:49 ` Eli Zaretskii
2021-07-22 5:09 ` Clément Pit-Claudel
2021-07-22 6:44 ` Eli Zaretskii
2021-07-22 14:43 ` Clément Pit-Claudel
2021-07-17 6:56 ` Eli Zaretskii
2021-07-20 16:28 ` Stephen Leake
2021-07-20 16:27 ` Stephen Leake
2021-07-20 16:25 ` Stephen Leake
2021-07-20 16:45 ` Eli Zaretskii
2021-07-21 15:49 ` Stephen Leake
2021-07-21 19:37 ` Eli Zaretskii
2021-07-24 2:00 ` Stephen Leake
2021-07-24 6:51 ` Eli Zaretskii
2021-07-25 16:16 ` Stephen Leake
[not found] <casouri/emacs/issues/5@github.com>
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
List information: https://www.gnu.org/software/emacs/
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=F95D0E9B-0B21-450A-B91D-87E9E05873CE@gmail.com \
--to=casouri@gmail.com \
--cc=cpitclaudel@gmail.com \
--cc=emacs-devel@gnu.org \
--cc=monnier@iro.umontreal.ca \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
Code repositories for project(s) associated with this public inbox
https://git.savannah.gnu.org/cgit/emacs.git
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).