unofficial mirror of emacs-devel@gnu.org 
 help / color / mirror / code / Atom feed
blob 7d1225161cc609f5dd96bcfa59fbbab597527937 9461 bytes (raw)
name: src/tree_sitter.c 	 # note: path name is non-authoritative(*)

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
 
/* Tree-sitter integration for GNU Emacs.

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/>.  */

#include <config.h>

#include <sys/types.h>
#include <sys/stat.h>
#include <sys/param.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

#include "lisp.h"
#include "buffer.h"
#include "coding.h"
#include "tree_sitter.h"

/* 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)
{
  /* 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);
    }
}

/* 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)
{
  ptrdiff_t new_end_byte = CHAR_TO_BYTE (charpos + lenins);
  Lisp_Object parser_list = Fsymbol_value (Qtree_sitter_parser_list);
  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);
      parser_list = Fcdr (parser_list);
    }
}

/* Parse the buffer.  We don't parse until we have to. When we have
to, we call this function to parse and update the tree.  */
void
ts_ensure_parsed (Lisp_Object parser)
{
  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);
  XTS_PARSER (parser)->tree = new_tree;
}

/* This is the read function provided to tree-sitter to read from a
   buffer.  It reads one character at a time and automatically skip
   the gap.  */
const char*
ts_read_buffer (void *buffer, uint32_t byte_index,
		TSPoint position, uint32_t *bytes_read)
{
  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.  */
  char *beg;
  int len;
  if (byte_pos >= Z_BYTE)
    {
      beg = "";
      len = 0;
    }
  else
    {
      beg = (char *) BYTE_POS_ADDR (byte_pos);
      len = next_char_len(byte_pos);
    }
  *bytes_read = (uint32_t) len;
  current_buffer = old_buffer;
  return beg;
}

/* Wrap the parser in a Lisp_Object to be used in the Lisp machine.  */
Lisp_Object
make_ts_parser (struct buffer *buffer, TSParser *parser, TSTree *tree)
{
  struct Lisp_TS_Parser *lisp_parser
    = ALLOCATE_PLAIN_PSEUDOVECTOR (struct Lisp_TS_Parser, PVEC_TS_PARSER);
  lisp_parser->buffer = buffer;
  lisp_parser->parser = parser;
  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;
  return make_lisp_ptr (lisp_parser, Lisp_Vectorlike);
}

/* Wrap the node in a Lisp_Object to be used in the Lisp machine.  */
Lisp_Object
make_ts_node (Lisp_Object parser, TSNode node)
{
  struct Lisp_TS_Node *lisp_node
    = ALLOCATE_PSEUDOVECTOR (struct Lisp_TS_Node, parser, PVEC_TS_NODE);
  lisp_node->parser = parser;
  lisp_node->node = node;
  return make_lisp_ptr (lisp_node, Lisp_Vectorlike);
}


DEFUN ("tree-sitter-create-parser",
       Ftree_sitter_create_parser, Stree_sitter_create_parser,
       2, 2, 0,
       doc: /* Create and return a parser in BUFFER for LANGUAGE.
The parser is automatically added to BUFFER's
`tree-sitter-parser-list'.  LANGUAGE should be the language provided
by a tree-sitter language dynamic module.  */)
  (Lisp_Object buffer, Lisp_Object language)
{
  CHECK_BUFFER(buffer);

  /* LANGUAGE is a USER_PTR that contains the pointer to a TSLanguage
     struct.  */
  TSParser *parser = ts_parser_new ();
  TSLanguage *lang = (XUSER_PTR (language)->p);
  ts_parser_set_language (parser, lang);

  Lisp_Object lisp_parser
    = make_ts_parser (XBUFFER(buffer), parser, NULL);

  // FIXME: Is this the correct way to set a buffer-local variable?
  struct buffer *old_buffer = current_buffer;
  set_buffer_internal (XBUFFER (buffer));

  Fset (Qtree_sitter_parser_list,
	Fcons (lisp_parser, Fsymbol_value (Qtree_sitter_parser_list)));

  set_buffer_internal (old_buffer);
  return lisp_parser;
}

DEFUN ("tree-sitter-parser-root-node",
       Ftree_sitter_parser_root_node, Stree_sitter_parser_root_node,
       1, 1, 0,
       doc: /* Return the root node of PARSER.  */)
  (Lisp_Object parser)
{
  ts_ensure_parsed(parser);
  TSNode root_node = ts_tree_root_node (XTS_PARSER (parser)->tree);
  return make_ts_node (parser, root_node);
}

DEFUN ("tree-sitter-parse", Ftree_sitter_parse, Stree_sitter_parse,
       2, 2, 0,
       doc: /* Parse STRING and return the root node.
LANGUAGE should be the language provided by a tree-sitter language
dynamic module.  */)
  (Lisp_Object string, Lisp_Object language)
{
  CHECK_STRING (string);

  /* LANGUAGE is a USER_PTR that contains the pointer to a TSLanguage
     struct.  */
  TSParser *parser = ts_parser_new ();
  TSLanguage *lang = (XUSER_PTR (language)->p);
  ts_parser_set_language (parser, lang);

  TSTree *tree = ts_parser_parse_string (parser, NULL,
					 SSDATA (string),
					 strlen (SSDATA (string)));

  /* See comment for ts_parser_parse in tree_sitter/api.h
     for possible reasons for a failure.  */
  if (tree == NULL)
    signal_error ("Failed to parse STRING", string);

  TSNode root_node = ts_tree_root_node (tree);

  Lisp_Object lisp_parser = make_ts_parser (NULL, parser, tree);
  Lisp_Object lisp_node = make_ts_node (lisp_parser, root_node);

  return lisp_node;
}

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)
{
  TSNode ts_node = XTS_NODE (node)->node;
  char *string = ts_node_string(ts_node);
  return make_string(string, strlen (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.  */)
  (Lisp_Object node)
{
  TSNode ts_node = XTS_NODE (node)->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);
}

DEFUN ("tree-sitter-node-child",
       Ftree_sitter_node_child, Stree_sitter_node_child, 2, 2, 0,
       doc: /* Return the Nth child of NODE.
Return nil if we couldn't find any.  */)
  (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);

  if (ts_node_is_null(child))
    return Qnil;

  return make_ts_node(XTS_NODE (node)->parser, child);
}

/* Initialize the tree-sitter routines.  */
void
syms_of_tree_sitter (void)
{
  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.
// TODO: more doc.
If you removed a parser from this list, do not put it back in.  */);
  Vtree_sitter_parser_list = Qnil;
  Fmake_variable_buffer_local (Qtree_sitter_parser_list);


  defsubr (&Stree_sitter_create_parser);
  defsubr (&Stree_sitter_parser_root_node);
  defsubr (&Stree_sitter_parse);
  defsubr (&Stree_sitter_node_string);
  defsubr (&Stree_sitter_node_parent);
  defsubr (&Stree_sitter_node_child);
}

debug log:

solving 7d1225161c ...
found 7d1225161c in https://yhetil.org/emacs-devel/258CB68D-1CC1-42C8-BDCD-2A8A8099B783@gmail.com/
found f2134c571a in https://yhetil.org/emacs-devel/9F1D903D-0BD2-44F5-B83C-4E9F3D12AC85@gmail.com/

applying [1/2] https://yhetil.org/emacs-devel/9F1D903D-0BD2-44F5-B83C-4E9F3D12AC85@gmail.com/
diff --git a/src/tree_sitter.c b/src/tree_sitter.c
new file mode 100644
index 0000000000..f2134c571a


applying [2/2] https://yhetil.org/emacs-devel/258CB68D-1CC1-42C8-BDCD-2A8A8099B783@gmail.com/
diff --git a/src/tree_sitter.c b/src/tree_sitter.c
index f2134c571a..7d1225161c 100644

Checking patch src/tree_sitter.c...
Applied patch src/tree_sitter.c cleanly.
Checking patch src/tree_sitter.c...
Applied patch src/tree_sitter.c cleanly.

index at:
100644 7d1225161cc609f5dd96bcfa59fbbab597527937	src/tree_sitter.c

(*) Git path names are given by the tree(s) the blob belongs to.
    Blobs themselves have no identifier aside from the hash of its contents.^

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).