@konomanoasa/tree-sitter-gitattributes 0.3.0 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/src/scanner.c CHANGED
@@ -54,17 +54,18 @@ enum Token {
54
54
  INCOMPLETE_SET_CLOSE,
55
55
  INVALID_ENCODING,
56
56
  NEGATIVE_PATTERN,
57
- INVALID_ESCAPE,
57
+ ENDED_ESCAPE,
58
58
  INCOMPLETE_ESCAPE,
59
59
  INVALID_QUOTED_ESCAPE,
60
60
  ENDED_QUOTED_ESCAPE,
61
61
  INCOMPLETE_QUOTED_ESCAPE,
62
- MISSING_QUOTE,
63
- INCOMPLETE_QUOTE,
64
- MISSING_SEPARATOR,
65
- MISSING_NAME,
66
- INCOMPLETE_NAME,
62
+ MISSING_QUOTE_CLOSE,
63
+ INCOMPLETE_QUOTE_CLOSE,
64
+ MISSING_ATTRIBUTE_SEPARATOR,
65
+ MISSING_ATTRIBUTE_NAME,
66
+ INCOMPLETE_ATTRIBUTE_NAME,
67
67
  INVALID_NAME_CHARACTER,
68
+ INCOMPLETE_NAME_CHARACTER,
68
69
  INVALID_NAME_ESCAPE,
69
70
  ERROR_SENTINEL
70
71
  };
@@ -74,17 +75,23 @@ enum Token {
74
75
  enum { DECODE_ERROR = -1, CURSOR_END = -2, INVALID_UNIT = -3 };
75
76
 
76
77
  typedef struct {
77
- uint32_t kind, end, opening_end, closing_start;
78
+ uint32_t kind, end;
78
79
  } Item;
79
80
 
80
- // Positions are line-relative character offsets; uint32_t avoids padding.
81
+ // Class bounds include brackets; inner bounds exclude both colons.
81
82
  typedef struct {
82
- uint32_t position, leading, line_end, content_start, content_end, kind;
83
+ uint32_t start, opening_end, closing_start, end;
84
+ } Class;
85
+
86
+ // Positions are line-relative character offsets.
87
+ typedef struct {
88
+ uint32_t position, leading, line_end, content_end;
83
89
  uint32_t token_end, name_start, name_end;
84
- uint32_t next, next_end, prefix_end, prefix_kind;
85
- uint32_t set_end, set_close_end, class_start, range_operator_end;
86
- Item class, lower, upper;
87
- uint32_t quoted, component_start, after_separator;
90
+ uint32_t next_end, prefix_end, set_end, set_close_end;
91
+ Class class;
92
+ uint32_t lower_end, range_operator_end, upper_end;
93
+ uint8_t kind, next, prefix_kind, lower_kind, upper_kind;
94
+ uint8_t quoted, component_start, after_separator;
88
95
  } Scanner;
89
96
 
90
97
  typedef char scanner_fits_buffer
@@ -98,7 +105,7 @@ typedef struct {
98
105
  } Cursor;
99
106
 
100
107
  static bool whitespace(int32_t c) {
101
- return c == ' ' || c == '\t' || c == '\v' || c == '\f' || c == '\r';
108
+ return c == ' ' || c == '\t' || c == '\r';
102
109
  }
103
110
 
104
111
  static bool alphanumeric(int32_t c) {
@@ -117,8 +124,7 @@ static void advance(Scanner *s, TSLexer *lexer) {
117
124
  }
118
125
 
119
126
  static bool emit(TSLexer *lexer, const bool *valid, enum Token token) {
120
- if (!valid[token])
121
- return false;
127
+ if (!valid[token]) return false;
122
128
  lexer->result_symbol = token;
123
129
  return true;
124
130
  }
@@ -128,6 +134,15 @@ absent(TSLexer *lexer, enum Token missing, enum Token incomplete) {
128
134
  return lexer->eof(lexer) ? incomplete : missing;
129
135
  }
130
136
 
137
+ // Include the next decoded character so edits invalidate the run.
138
+ static bool invalid_run(Scanner *s, TSLexer *lexer, const bool *valid) {
139
+ do advance(s, lexer);
140
+ while (!lexer->eof(lexer) && lexer->lookahead == DECODE_ERROR);
141
+ lexer->mark_end(lexer);
142
+ if (!lexer->eof(lexer)) lexer->advance(lexer, false);
143
+ return emit(lexer, valid, INVALID_ENCODING);
144
+ }
145
+
131
146
  static bool consume(
132
147
  Scanner *s,
133
148
  TSLexer *lexer,
@@ -135,18 +150,9 @@ static bool consume(
135
150
  enum Token token,
136
151
  uint32_t end
137
152
  ) {
138
- if (token == INVALID_ENCODING) {
139
- do {
140
- advance(s, lexer);
141
- } while (!lexer->eof(lexer) && lexer->lookahead == DECODE_ERROR);
142
- } else {
143
- while (s->position < end)
144
- advance(s, lexer);
145
- }
153
+ if (token == INVALID_ENCODING) return invalid_run(s, lexer, valid);
154
+ while (s->position < end) advance(s, lexer);
146
155
  lexer->mark_end(lexer);
147
- // Include the next decoded character so edits invalidate this run.
148
- if (token == INVALID_ENCODING && !lexer->eof(lexer))
149
- lexer->advance(lexer, false);
150
156
  return emit(lexer, valid, token);
151
157
  }
152
158
 
@@ -157,8 +163,7 @@ static bool text_run(
157
163
  enum Token token,
158
164
  uint32_t end
159
165
  ) {
160
- if (lexer->lookahead == DECODE_ERROR)
161
- return consume(s, lexer, valid, INVALID_ENCODING, end);
166
+ if (lexer->lookahead == DECODE_ERROR) return invalid_run(s, lexer, valid);
162
167
  while (s->position < end && lexer->lookahead != DECODE_ERROR)
163
168
  advance(s, lexer);
164
169
  lexer->mark_end(lexer);
@@ -170,85 +175,85 @@ static void raw_step(Cursor *r) {
170
175
  r->raw++;
171
176
  }
172
177
 
178
+ static int32_t simple_escape(int32_t c) {
179
+ switch (c) {
180
+ case 'a':
181
+ return '\a';
182
+ case 'b':
183
+ return '\b';
184
+ case 't':
185
+ return '\t';
186
+ case 'n':
187
+ return '\n';
188
+ case 'v':
189
+ return '\v';
190
+ case 'f':
191
+ return '\f';
192
+ case 'r':
193
+ return '\r';
194
+ case '\\':
195
+ case '"':
196
+ return c;
197
+ default:
198
+ return INVALID_UNIT;
199
+ }
200
+ }
201
+
202
+ // The cursor has passed the backslash.
203
+ static void quoted_escape(Cursor *r) {
204
+ const int32_t c = r->lexer->lookahead;
205
+ r->kind = QUOTED_ESCAPE;
206
+ if (r->raw >= r->limit) {
207
+ r->c = INVALID_UNIT;
208
+ r->kind = absent(r->lexer, ENDED_QUOTED_ESCAPE, INCOMPLETE_QUOTED_ESCAPE);
209
+ } else if (c == DECODE_ERROR) {
210
+ r->c = INVALID_UNIT;
211
+ r->kind = ESCAPE_PREFIX;
212
+ } else if (c >= '0' && c <= '3') {
213
+ int32_t value = 0;
214
+ unsigned digits = 0;
215
+ while (digits < 3 && r->raw < r->limit) {
216
+ const int32_t digit = r->lexer->lookahead;
217
+ if (digit < '0' || digit > '7') break;
218
+ value = value * 8 + digit - '0';
219
+ raw_step(r);
220
+ digits++;
221
+ }
222
+ if (digits == 3) r->c = value;
223
+ else {
224
+ r->c = INVALID_UNIT;
225
+ r->kind = r->raw == r->limit
226
+ ? absent(r->lexer, ENDED_QUOTED_ESCAPE, INCOMPLETE_QUOTED_ESCAPE)
227
+ : ENDED_QUOTED_ESCAPE;
228
+ }
229
+ } else {
230
+ raw_step(r);
231
+ r->c = simple_escape(c);
232
+ if (r->c == INVALID_UNIT) r->kind = INVALID_QUOTED_ESCAPE;
233
+ }
234
+ }
235
+
173
236
  static void step(Cursor *r) {
174
237
  r->start = r->end = r->raw;
175
238
  r->kind = 0;
176
239
  r->c = CURSOR_END;
177
- if (r->raw >= r->limit)
178
- return;
179
- int32_t c = r->lexer->lookahead;
240
+ if (r->raw >= r->limit) return;
241
+ const int32_t c = r->lexer->lookahead;
180
242
  raw_step(r);
181
243
  r->c = c;
182
244
  if (c == DECODE_ERROR) {
183
245
  r->kind = INVALID_ENCODING;
184
246
  while (r->raw < r->limit && r->lexer->lookahead == DECODE_ERROR)
185
247
  raw_step(r);
186
- } else if (r->s->quoted && r->start < r->s->content_end && c == '\\') {
187
- r->kind = QUOTED_ESCAPE;
188
- c = r->lexer->lookahead;
189
- if (r->raw >= r->limit) {
190
- r->c = INVALID_UNIT;
191
- r->kind = absent(r->lexer, ENDED_QUOTED_ESCAPE, INCOMPLETE_QUOTED_ESCAPE);
192
- } else if (c == DECODE_ERROR) {
193
- r->c = INVALID_UNIT;
194
- r->kind = ESCAPE_PREFIX;
195
- } else if (c >= '0' && c <= '3') {
196
- int32_t value = 0;
197
- unsigned digits = 0;
198
- while (digits < 3 && r->raw < r->limit) {
199
- const int32_t digit = r->lexer->lookahead;
200
- if (digit < '0' || digit > '7')
201
- break;
202
- value = value * 8 + digit - '0';
203
- raw_step(r);
204
- digits++;
205
- }
206
- if (digits == 3)
207
- r->c = value;
208
- else {
209
- r->c = INVALID_UNIT;
210
- r->kind = r->raw == r->limit
211
- ? absent(r->lexer, ENDED_QUOTED_ESCAPE, INCOMPLETE_QUOTED_ESCAPE)
212
- : ENDED_QUOTED_ESCAPE;
213
- }
214
- } else {
215
- raw_step(r);
216
- switch (c) {
217
- case 'a':
218
- r->c = '\a';
219
- break;
220
- case 'b':
221
- r->c = '\b';
222
- break;
223
- case 't':
224
- r->c = '\t';
225
- break;
226
- case 'n':
227
- r->c = '\n';
228
- break;
229
- case 'v':
230
- r->c = '\v';
231
- break;
232
- case 'f':
233
- r->c = '\f';
234
- break;
235
- case 'r':
236
- r->c = '\r';
237
- break;
238
- case '\\':
239
- case '"':
240
- r->c = c;
241
- break;
242
- default:
243
- r->c = INVALID_UNIT;
244
- r->kind = INVALID_QUOTED_ESCAPE;
245
- break;
246
- }
247
- }
248
- }
248
+ } else if (r->s->quoted && r->start < r->s->content_end && c == '\\')
249
+ quoted_escape(r);
249
250
  r->end = r->raw;
250
251
  }
251
252
 
253
+ static uint32_t content_start(const Scanner *s) {
254
+ return s->leading + s->quoted;
255
+ }
256
+
252
257
  static Cursor cursor(const Scanner *s, TSLexer *lexer, uint32_t limit) {
253
258
  Cursor r = {.s = s, .lexer = lexer, .raw = s->position, .limit = limit};
254
259
  step(&r);
@@ -256,10 +261,9 @@ static Cursor cursor(const Scanner *s, TSLexer *lexer, uint32_t limit) {
256
261
  }
257
262
 
258
263
  static bool start_line(Scanner *s, TSLexer *lexer, const bool *valid) {
259
- if (lexer->eof(lexer))
260
- return false;
264
+ if (lexer->eof(lexer)) return false;
261
265
  memset(s, 0, sizeof(*s));
262
- s->class_start = NONE;
266
+ s->class.start = NONE;
263
267
  s->component_start = true;
264
268
  int32_t last = 0;
265
269
  lexer->mark_end(lexer);
@@ -270,47 +274,39 @@ static bool start_line(Scanner *s, TSLexer *lexer, const bool *valid) {
270
274
  s->leading = s->position;
271
275
  s->kind = lexer->lookahead == '#' ? COMMENT_START : RULE_START;
272
276
  s->quoted = lexer->lookahead == '"';
273
- s->content_start = s->leading + (s->quoted ? 1 : 0);
277
+ const uint32_t start = content_start(s);
274
278
  s->content_end = NONE;
275
279
  bool escaped = false;
276
280
  while (!lexer->eof(lexer) && lexer->lookahead != '\n') {
277
281
  const int32_t c = lexer->lookahead;
278
- if (s->position >= s->content_start && s->content_end == NONE) {
282
+ if (s->position >= start && s->content_end == NONE) {
279
283
  if (s->quoted) {
280
- if (c == '"' && !escaped)
281
- s->content_end = s->position;
284
+ if (c == '"' && !escaped) s->content_end = s->position;
282
285
  escaped = c == '\\' && !escaped;
283
- } else if (whitespace(c))
284
- s->content_end = s->position;
286
+ } else if (whitespace(c)) s->content_end = s->position;
285
287
  }
286
288
  last = c;
287
289
  advance(s, lexer);
288
290
  }
289
291
  s->line_end = s->position;
290
- if (last == '\r' && lexer->lookahead == '\n')
291
- s->line_end--;
292
- if (s->leading > s->line_end)
293
- s->leading = s->line_end;
294
- if (s->content_end == NONE || s->content_end > s->line_end)
295
- s->content_end = s->line_end;
296
- if (s->leading >= s->line_end)
297
- s->kind = BLANK_START;
292
+ if (last == '\r' && lexer->lookahead == '\n') s->line_end--;
293
+ if (s->leading > s->line_end) s->leading = s->line_end;
294
+ if (s->content_end == NONE) s->content_end = s->line_end;
295
+ if (s->leading >= s->line_end) s->kind = BLANK_START;
298
296
  s->position = 0;
299
297
  return emit(lexer, valid, LINE_START);
300
298
  }
301
299
 
302
300
  static bool start_content(Scanner *s, TSLexer *lexer, const bool *valid) {
303
- while (s->position < s->leading)
304
- advance(s, lexer);
301
+ while (s->position < s->leading) advance(s, lexer);
305
302
  lexer->mark_end(lexer);
306
303
  if (s->kind == RULE_START) {
307
304
  // The opening quote is lookahead; QUOTE_OPEN consumes it later.
308
- if (s->quoted)
309
- lexer->advance(lexer, false);
305
+ if (s->quoted) lexer->advance(lexer, false);
310
306
  Cursor r = {
311
307
  .s = s,
312
308
  .lexer = lexer,
313
- .raw = s->content_start,
309
+ .raw = content_start(s),
314
310
  .limit = s->content_end
315
311
  };
316
312
  static const char marker[] = "[attr]";
@@ -333,25 +329,21 @@ static bool start_content(Scanner *s, TSLexer *lexer, const bool *valid) {
333
329
 
334
330
  static void skip_escaped(Cursor *r, Cursor *last, uint32_t *candidate) {
335
331
  *last = *r;
336
- if (r->c == ']')
337
- *candidate = NONE;
332
+ if (r->c == ']') *candidate = NONE;
338
333
  step(r);
339
334
  }
340
335
 
341
- // Escapes and range upper endpoints take precedence over class candidates.
342
- static bool next_class(Cursor *r, bool lower, uint32_t *start, Item *class) {
343
- Item a = {CLASS_OPEN, 0, 0, 0};
344
- uint32_t candidate = NONE;
336
+ static bool next_class(Cursor *r, bool lower, Class *class) {
337
+ Class a = {NONE, 0, 0, 0};
345
338
  Cursor last = *r;
346
339
  while (r->c != CURSOR_END) {
347
340
  const int32_t here = r->c;
348
341
  if (here == ']') {
349
- if (candidate == NONE || last.c != ':' || last.end == a.opening_end)
342
+ if (a.start == NONE || last.c != ':' || last.end == a.opening_end)
350
343
  return false;
351
344
  a.closing_start = last.start;
352
345
  a.end = r->end;
353
346
  step(r);
354
- *start = candidate;
355
347
  *class = a;
356
348
  return true;
357
349
  }
@@ -359,21 +351,19 @@ static bool next_class(Cursor *r, bool lower, uint32_t *start, Item *class) {
359
351
  step(r);
360
352
  if (here == '\\') {
361
353
  lower = r->c >= 0;
362
- if (r->c != CURSOR_END)
363
- skip_escaped(r, &last, &candidate);
354
+ if (r->c != CURSOR_END) skip_escaped(r, &last, &a.start);
364
355
  continue;
365
356
  }
366
357
  if (here == '-' && lower && r->c != CURSOR_END && r->c != ']') {
367
358
  const int32_t upper = r->c;
368
- skip_escaped(r, &last, &candidate);
369
- if (upper == '\\' && r->c != CURSOR_END)
370
- skip_escaped(r, &last, &candidate);
359
+ skip_escaped(r, &last, &a.start);
360
+ if (upper == '\\' && r->c != CURSOR_END) skip_escaped(r, &last, &a.start);
371
361
  lower = false;
372
362
  continue;
373
363
  }
374
364
  lower = here >= 0;
375
- if (candidate == NONE && here == '[' && r->c == ':') {
376
- candidate = last.start;
365
+ if (a.start == NONE && here == '[' && r->c == ':') {
366
+ a.start = last.start;
377
367
  a.opening_end = r->end;
378
368
  }
379
369
  }
@@ -382,20 +372,14 @@ static bool next_class(Cursor *r, bool lower, uint32_t *start, Item *class) {
382
372
 
383
373
  static void find_set(Scanner *s, Cursor *r) {
384
374
  s->set_end = s->set_close_end = s->content_end;
385
- s->class_start = NONE;
386
- if (r->c == '!' || r->c == '^')
387
- step(r);
375
+ s->class.start = NONE;
376
+ if (r->c == '!' || r->c == '^') step(r);
388
377
  bool lower = r->c == ']';
389
- if (lower)
390
- step(r);
391
- Item class;
392
- uint32_t class_start;
393
- while (next_class(r, lower, &class_start, &class)) {
378
+ if (lower) step(r);
379
+ Class class;
380
+ while (next_class(r, lower, &class)) {
394
381
  lower = false;
395
- if (s->class_start == NONE) {
396
- s->class_start = class_start;
397
- s->class = class;
398
- }
382
+ if (s->class.start == NONE) s->class = class;
399
383
  }
400
384
  if (r->c == ']') {
401
385
  s->set_end = r->start;
@@ -404,25 +388,20 @@ static void find_set(Scanner *s, Cursor *r) {
404
388
  }
405
389
 
406
390
  static Item item(Cursor *r) {
407
- Item a = {SET_TEXT, 0, 0, 0};
408
- if (r->c == CURSOR_END)
409
- return a;
391
+ Item a = {SET_TEXT, 0};
410
392
  const int32_t c = r->c;
411
393
  const uint32_t end = r->end;
412
- if (c == '[' && r->start == r->s->class_start) {
413
- a = r->s->class;
414
- while (r->c != CURSOR_END && r->start < a.end)
415
- step(r);
394
+ if (c == '[' && r->start == r->s->class.start) {
395
+ a = (Item){CLASS_OPEN, r->s->class.end};
396
+ while (r->c != CURSOR_END && r->start < a.end) step(r);
416
397
  return a;
417
398
  }
418
- if (r->kind)
419
- a.kind = r->kind;
399
+ if (r->kind) a.kind = r->kind;
420
400
  step(r);
421
401
  if (c == '\\') {
422
402
  if (r->c == CURSOR_END)
423
- a.kind = absent(r->lexer, INVALID_ESCAPE, INCOMPLETE_ESCAPE);
424
- else if (r->c < 0)
425
- a.kind = ESCAPE_PREFIX;
403
+ a.kind = absent(r->lexer, ENDED_ESCAPE, INCOMPLETE_ESCAPE);
404
+ else if (r->c < 0) a.kind = ESCAPE_PREFIX;
426
405
  else {
427
406
  a.kind = ESCAPE;
428
407
  a.end = r->end;
@@ -435,18 +414,15 @@ static Item item(Cursor *r) {
435
414
  }
436
415
 
437
416
  static bool endpoint(Item a) {
438
- return a.end &&
439
- (a.kind == SET_TEXT || a.kind == ESCAPE || a.kind == QUOTED_ESCAPE);
417
+ return a.kind == SET_TEXT || a.kind == ESCAPE || a.kind == QUOTED_ESCAPE;
440
418
  }
441
419
 
442
420
  static bool literal_boundary(const Cursor *r) {
443
- if (r->c < 0 || r->kind == QUOTED_ESCAPE)
444
- return true;
421
+ if (r->c < 0 || r->kind == QUOTED_ESCAPE) return true;
445
422
  const int32_t c = r->c;
446
423
  return c == '\\' || c == '*' || c == '?' || c == '/' || c == '[';
447
424
  }
448
425
 
449
- // Keep the backslash in its owner; report the following unit's own issue.
450
426
  static void hold_prefix(Scanner *s, Cursor *r, uint32_t prefix_end) {
451
427
  s->next = ESCAPE_PREFIX;
452
428
  s->prefix_end = prefix_end;
@@ -461,28 +437,22 @@ static void hold_prefix(Scanner *s, Cursor *r, uint32_t prefix_end) {
461
437
 
462
438
  // Consume lookahead units fully so edits inside them invalidate the decision.
463
439
  static bool prepare_glob(Scanner *s, TSLexer *lexer, const bool *valid) {
464
- lexer->mark_end(lexer);
465
440
  Cursor r = cursor(s, lexer, s->content_end);
466
441
  bool separator = r.c == '/';
467
442
  const uint32_t start = s->position;
468
443
  s->next_end = r.end;
469
444
  if (r.c < 0) {
470
- if (r.kind == ESCAPE_PREFIX)
471
- hold_prefix(s, &r, r.end);
472
- else
473
- s->next = r.kind;
474
- } else if (r.c == '!' && start == s->content_start)
445
+ if (r.kind == ESCAPE_PREFIX) hold_prefix(s, &r, r.end);
446
+ else s->next = r.kind;
447
+ } else if (r.c == '!' && start == content_start(s))
475
448
  s->next = NEGATIVE_PATTERN;
476
- else if (r.c == '?')
477
- s->next = SINGLE_CHARACTER;
478
- else if (r.c == '/')
479
- s->next = PATH_SEPARATOR;
449
+ else if (r.c == '?') s->next = SINGLE_CHARACTER;
450
+ else if (r.c == '/') s->next = PATH_SEPARATOR;
480
451
  else if (r.c == '\\') {
481
452
  step(&r);
482
453
  if (r.c == CURSOR_END)
483
- s->next = absent(lexer, INVALID_ESCAPE, INCOMPLETE_ESCAPE);
484
- else if (r.c < 0)
485
- hold_prefix(s, &r, s->next_end);
454
+ s->next = absent(lexer, ENDED_ESCAPE, INCOMPLETE_ESCAPE);
455
+ else if (r.c < 0) hold_prefix(s, &r, s->next_end);
486
456
  else {
487
457
  s->next = ESCAPE;
488
458
  s->next_end = r.end;
@@ -497,12 +467,10 @@ static bool prepare_glob(Scanner *s, TSLexer *lexer, const bool *valid) {
497
467
  }
498
468
  const bool pair = count == 2 && s->component_start;
499
469
  const bool tail = r.c == CURSOR_END && s->after_separator;
500
- if (r.c == '\\')
501
- step(&r);
470
+ if (r.c == '\\') step(&r);
502
471
  const bool slash = r.c == '/';
503
472
  s->next = pair && (slash || tail) ? RECURSIVE_WILDCARD : WILDCARD;
504
- } else if (r.kind == QUOTED_ESCAPE && r.c != '[')
505
- s->next = QUOTED_ESCAPE;
473
+ } else if (r.kind == QUOTED_ESCAPE && r.c != '[') s->next = QUOTED_ESCAPE;
506
474
  else if (r.c == '[') {
507
475
  s->next = SET_OPEN;
508
476
  step(&r);
@@ -521,13 +489,11 @@ static bool prepare_glob(Scanner *s, TSLexer *lexer, const bool *valid) {
521
489
  return emit(lexer, valid, LITERAL);
522
490
  }
523
491
  s->component_start = separator || s->next == NEGATIVE_PATTERN;
524
- if (separator)
525
- s->after_separator = true;
492
+ if (separator) s->after_separator = true;
526
493
  return emit(lexer, valid, GLOB_START);
527
494
  }
528
495
 
529
496
  static bool prepare_set(Scanner *s, TSLexer *lexer, const bool *valid) {
530
- lexer->mark_end(lexer);
531
497
  Cursor r = cursor(s, lexer, s->set_end);
532
498
  const uint32_t start = s->position;
533
499
  if (valid[SET_NEGATION] && (r.c == '!' || r.c == '^')) {
@@ -537,7 +503,6 @@ static bool prepare_set(Scanner *s, TSLexer *lexer, const bool *valid) {
537
503
  }
538
504
  s->next = SET_TEXT;
539
505
  s->next_end = start;
540
- uint32_t here = start;
541
506
  Item current = item(&r);
542
507
  for (;;) {
543
508
  if (endpoint(current) && r.c == '-' && r.end < s->set_end) {
@@ -546,13 +511,14 @@ static bool prepare_set(Scanner *s, TSLexer *lexer, const bool *valid) {
546
511
  step(&r);
547
512
  const Item upper = item(&r);
548
513
  if (endpoint(upper)) {
549
- if (here == start) {
514
+ if (s->next_end == start) {
550
515
  s->next = RANGE_START;
551
- s->lower = current;
552
- s->upper = upper;
516
+ s->lower_kind = current.kind;
517
+ s->lower_end = current.end;
553
518
  s->range_operator_end = operator_end;
554
- } else
555
- s->next_end = here;
519
+ s->upper_kind = upper.kind;
520
+ s->upper_end = upper.end;
521
+ }
556
522
  break;
557
523
  }
558
524
  if (current.kind == SET_TEXT) {
@@ -561,32 +527,31 @@ static bool prepare_set(Scanner *s, TSLexer *lexer, const bool *valid) {
561
527
  break;
562
528
  }
563
529
  s->next_end = operator_end;
564
- here = operator_end;
565
530
  current = upper;
566
531
  continue;
567
532
  }
568
533
  }
569
534
  if (current.kind != SET_TEXT) {
570
- if (here == start) {
535
+ if (s->next_end == start) {
571
536
  s->next = current.kind;
572
537
  s->next_end = current.end;
573
- if (current.kind == ESCAPE_PREFIX)
574
- hold_prefix(s, &r, current.end);
575
- } else
576
- s->next_end = here;
538
+ if (current.kind == ESCAPE_PREFIX) hold_prefix(s, &r, current.end);
539
+ }
577
540
  break;
578
541
  }
579
542
  s->next_end = current.end;
580
- here = r.start;
581
- if (r.c == CURSOR_END)
582
- break;
543
+ if (r.c == CURSOR_END) break;
583
544
  current = item(&r);
584
545
  }
585
546
  return emit(lexer, valid, SET_ITEM_START);
586
547
  }
587
548
 
588
- static bool
589
- unit_token(Scanner *s, TSLexer *lexer, const bool *valid, enum Token token) {
549
+ static bool class_delimiter(
550
+ Scanner *s,
551
+ TSLexer *lexer,
552
+ const bool *valid,
553
+ enum Token token
554
+ ) {
590
555
  Cursor r = cursor(s, lexer, s->class.end);
591
556
  s->position = r.end;
592
557
  lexer->mark_end(lexer);
@@ -597,18 +562,15 @@ static bool class_piece(Scanner *s, TSLexer *lexer, const bool *valid) {
597
562
  if (s->position < s->class.opening_end)
598
563
  return consume(s, lexer, valid, CLASS_OPEN, s->class.opening_end);
599
564
  if (s->position == s->class.closing_start)
600
- return unit_token(s, lexer, valid, CLASS_CLOSE);
565
+ return class_delimiter(s, lexer, valid, CLASS_CLOSE);
601
566
  if (s->position > s->class.closing_start) {
602
- while (s->position < s->class.end)
603
- advance(s, lexer);
567
+ while (s->position < s->class.end) advance(s, lexer);
604
568
  lexer->mark_end(lexer);
605
569
  Cursor r = cursor(s, lexer, s->set_end);
606
- if (!next_class(&r, false, &s->class_start, &s->class))
607
- s->class_start = NONE;
570
+ if (!next_class(&r, false, &s->class)) s->class.start = NONE;
608
571
  return emit(lexer, valid, COMPOUND_CLOSE);
609
572
  }
610
- if (lexer->lookahead == DECODE_ERROR)
611
- return consume(s, lexer, valid, INVALID_ENCODING, s->class.closing_start);
573
+ if (lexer->lookahead == DECODE_ERROR) return invalid_run(s, lexer, valid);
612
574
  Cursor r = cursor(s, lexer, s->class.closing_start);
613
575
  const enum Token token = r.kind ? (enum Token)r.kind : CLASS_NAME;
614
576
  uint32_t end = r.end;
@@ -625,27 +587,40 @@ static bool class_piece(Scanner *s, TSLexer *lexer, const bool *valid) {
625
587
  return emit(lexer, valid, token);
626
588
  }
627
589
 
590
+ static bool range_piece(Scanner *s, TSLexer *lexer, const bool *valid) {
591
+ if (s->position == s->lower_end)
592
+ return consume(s, lexer, valid, RANGE_OPERATOR, s->range_operator_end);
593
+ const bool lower = s->position < s->lower_end;
594
+ const enum Token kind = (enum Token)(lower ? s->lower_kind : s->upper_kind);
595
+ return consume(
596
+ s,
597
+ lexer,
598
+ valid,
599
+ kind == SET_TEXT ? RANGE_CHARACTER : kind,
600
+ lower ? s->lower_end : s->upper_end
601
+ );
602
+ }
603
+
604
+ static enum Token name_token(const Cursor *r, bool first) {
605
+ if (r->c < 0) return (enum Token)r->kind;
606
+ if (!r->kind && r->c == '\r' && r->lexer->eof(r->lexer))
607
+ return INCOMPLETE_NAME_CHARACTER;
608
+ if (!name_character(r->c, first))
609
+ return r->kind == QUOTED_ESCAPE ? INVALID_NAME_ESCAPE
610
+ : INVALID_NAME_CHARACTER;
611
+ return r->kind == QUOTED_ESCAPE ? QUOTED_ESCAPE : NAME_TEXT;
612
+ }
613
+
628
614
  static bool name_piece(Scanner *s, TSLexer *lexer, const bool *valid) {
629
- if (lexer->lookahead == DECODE_ERROR)
630
- return consume(s, lexer, valid, INVALID_ENCODING, s->name_end);
615
+ if (lexer->lookahead == DECODE_ERROR) return invalid_run(s, lexer, valid);
631
616
  Cursor r = cursor(s, lexer, s->name_end);
632
- enum Token token;
633
- if (r.kind == ESCAPE_PREFIX)
634
- s->next = INVALID_ENCODING;
635
- if (r.c < 0)
636
- token = (enum Token)r.kind;
637
- else if (!name_character(r.c, s->position == s->name_start))
638
- token =
639
- r.kind == QUOTED_ESCAPE ? INVALID_NAME_ESCAPE : INVALID_NAME_CHARACTER;
640
- else
641
- token = r.kind == QUOTED_ESCAPE ? QUOTED_ESCAPE : NAME_TEXT;
617
+ if (r.kind == ESCAPE_PREFIX) s->next = INVALID_ENCODING;
618
+ const enum Token token = name_token(&r, s->position == s->name_start);
642
619
  uint32_t end = r.end;
643
620
  lexer->mark_end(lexer);
644
621
  if (token == NAME_TEXT || token == INVALID_NAME_CHARACTER) {
645
622
  step(&r);
646
- while (
647
- r.c >= 0 && !r.kind && name_character(r.c, false) == (token == NAME_TEXT)
648
- ) {
623
+ while (name_token(&r, false) == token) {
649
624
  end = r.end;
650
625
  lexer->mark_end(lexer);
651
626
  step(&r);
@@ -656,27 +631,22 @@ static bool name_piece(Scanner *s, TSLexer *lexer, const bool *valid) {
656
631
  }
657
632
 
658
633
  static bool prepare_attribute(Scanner *s, TSLexer *lexer, const bool *valid) {
659
- lexer->mark_end(lexer);
660
634
  Cursor r = cursor(s, lexer, s->line_end);
661
635
  const bool modified = r.c == '-' || r.c == '!';
662
636
  s->name_start = s->position + (modified ? 1 : 0);
663
637
  s->name_end = NONE;
664
638
  while (r.c != CURSOR_END && !whitespace(r.c)) {
665
- if (r.c == '=' && s->name_end == NONE)
666
- s->name_end = r.start;
639
+ if (r.c == '=' && s->name_end == NONE) s->name_end = r.start;
667
640
  step(&r);
668
641
  }
669
642
  s->token_end = r.start;
670
- if (s->name_end == NONE)
671
- s->name_end = r.start;
643
+ if (s->name_end == NONE) s->name_end = r.start;
672
644
  return emit(lexer, valid, ATTRIBUTE_START);
673
645
  }
674
646
 
675
647
  static bool scan(Scanner *s, TSLexer *lexer, const bool *valid) {
676
- if (valid[ERROR_SENTINEL])
677
- return false;
678
- if (valid[LINE_START])
679
- return start_line(s, lexer, valid);
648
+ if (valid[ERROR_SENTINEL]) return false;
649
+ if (valid[LINE_START]) return start_line(s, lexer, valid);
680
650
  if (
681
651
  valid[BLANK_START] ||
682
652
  valid[COMMENT_START] ||
@@ -699,30 +669,52 @@ static bool scan(Scanner *s, TSLexer *lexer, const bool *valid) {
699
669
  absent(lexer, MISSING_SET_CLOSE, INCOMPLETE_SET_CLOSE)
700
670
  );
701
671
  }
702
- if (valid[ATTRIBUTE_START])
703
- return prepare_attribute(s, lexer, valid);
672
+ if (valid[ATTRIBUTE_START]) return prepare_attribute(s, lexer, valid);
704
673
  if (valid[ATTRIBUTE_END] && s->position == s->token_end)
705
674
  return emit(lexer, valid, ATTRIBUTE_END);
706
675
  const bool at_name =
707
676
  s->position == s->name_start && s->position < s->name_end;
708
- if (valid[NAME_START] && at_name)
709
- return emit(lexer, valid, NAME_START);
677
+ if (valid[NAME_START] && at_name) return emit(lexer, valid, NAME_START);
710
678
  if (valid[NAME_END] && s->position == s->name_end)
711
679
  return emit(lexer, valid, NAME_END);
712
680
  if (valid[VALUE_START] && s->position < s->token_end)
713
681
  return emit(lexer, valid, VALUE_START);
714
682
  if (valid[VALUE_END] && s->position == s->token_end)
715
683
  return emit(lexer, valid, VALUE_END);
716
- if (valid[MISSING_NAME] && s->position == s->name_end)
717
- return emit(lexer, valid, absent(lexer, MISSING_NAME, INCOMPLETE_NAME));
684
+ if (valid[MISSING_ATTRIBUTE_NAME] && s->position == s->name_end)
685
+ return emit(
686
+ lexer,
687
+ valid,
688
+ absent(lexer, MISSING_ATTRIBUTE_NAME, INCOMPLETE_ATTRIBUTE_NAME)
689
+ );
690
+ if (
691
+ (valid[UNSET_MARKER] || valid[UNSPECIFIED_MARKER]) &&
692
+ s->position < s->name_start
693
+ )
694
+ return consume(
695
+ s,
696
+ lexer,
697
+ valid,
698
+ lexer->lookahead == '-' ? UNSET_MARKER : UNSPECIFIED_MARKER,
699
+ s->position + 1
700
+ );
701
+ if (valid[NAME_TEXT]) return name_piece(s, lexer, valid);
702
+ if (valid[VALUE_TEXT])
703
+ return text_run(s, lexer, valid, VALUE_TEXT, s->token_end);
704
+ if (valid[ASSIGNMENT_OPERATOR] && lexer->lookahead == '=')
705
+ return consume(s, lexer, valid, ASSIGNMENT_OPERATOR, s->position + 1);
718
706
  if (
719
- (valid[QUOTE_CLOSE] || valid[MISSING_QUOTE]) &&
707
+ (valid[QUOTE_CLOSE] || valid[MISSING_QUOTE_CLOSE]) &&
720
708
  s->quoted &&
721
709
  s->position == s->content_end
722
710
  ) {
723
711
  if (s->content_end < s->line_end)
724
712
  return consume(s, lexer, valid, QUOTE_CLOSE, s->position + 1);
725
- return emit(lexer, valid, absent(lexer, MISSING_QUOTE, INCOMPLETE_QUOTE));
713
+ return emit(
714
+ lexer,
715
+ valid,
716
+ absent(lexer, MISSING_QUOTE_CLOSE, INCOMPLETE_QUOTE_CLOSE)
717
+ );
726
718
  }
727
719
  if (valid[PATTERN_END] && s->position >= s->content_end)
728
720
  return emit(lexer, valid, PATTERN_END);
@@ -737,56 +729,27 @@ static bool scan(Scanner *s, TSLexer *lexer, const bool *valid) {
737
729
  return consume(s, lexer, valid, QUOTE_OPEN, s->position + 1);
738
730
  if (valid[MACRO_MARKER])
739
731
  return consume(s, lexer, valid, MACRO_MARKER, s->name_start);
740
- if (
741
- (valid[UNSET_MARKER] || valid[UNSPECIFIED_MARKER]) &&
742
- s->position < s->name_start
743
- )
744
- return consume(
745
- s,
746
- lexer,
747
- valid,
748
- lexer->lookahead == '-' ? UNSET_MARKER : UNSPECIFIED_MARKER,
749
- s->position + 1
750
- );
751
- if (valid[NAME_TEXT])
752
- return name_piece(s, lexer, valid);
753
- if (valid[VALUE_TEXT])
754
- return text_run(s, lexer, valid, VALUE_TEXT, s->token_end);
755
- if (valid[ASSIGNMENT_OPERATOR] && lexer->lookahead == '=')
756
- return consume(s, lexer, valid, ASSIGNMENT_OPERATOR, s->position + 1);
757
732
  if (valid[SEPARATOR] || valid[LAYOUT]) {
758
733
  if (!whitespace(lexer->lookahead))
759
- return emit(lexer, valid, MISSING_SEPARATOR);
734
+ return emit(lexer, valid, MISSING_ATTRIBUTE_SEPARATOR);
760
735
  while (s->position < s->line_end && whitespace(lexer->lookahead))
761
736
  advance(s, lexer);
762
737
  lexer->mark_end(lexer);
763
738
  return emit(lexer, valid, s->position < s->line_end ? SEPARATOR : LAYOUT);
764
739
  }
765
- if (valid[GLOB_START])
766
- return prepare_glob(s, lexer, valid);
740
+ if (valid[GLOB_START]) return prepare_glob(s, lexer, valid);
767
741
  if (valid[SET_ITEM_START] || valid[SET_NEGATION])
768
742
  return prepare_set(s, lexer, valid);
769
- if (s->class_start < s->position && s->position < s->class.end)
743
+ if (s->class.start < s->position && s->position < s->class.end)
770
744
  return class_piece(s, lexer, valid);
771
745
  if (s->next == RANGE_START) {
772
746
  s->next = RANGE_CHARACTER;
773
747
  return emit(lexer, valid, RANGE_START);
774
748
  }
775
- if (s->upper.end > s->position) {
776
- if (s->position == s->lower.end)
777
- return consume(s, lexer, valid, RANGE_OPERATOR, s->range_operator_end);
778
- const Item a = s->position < s->lower.end ? s->lower : s->upper;
779
- return consume(
780
- s,
781
- lexer,
782
- valid,
783
- a.kind == SET_TEXT ? RANGE_CHARACTER : (enum Token)a.kind,
784
- a.end
785
- );
786
- }
749
+ if (s->upper_end > s->position) return range_piece(s, lexer, valid);
787
750
  const enum Token token = (enum Token)s->next;
788
751
  if (token == CLASS_OPEN)
789
- return unit_token(s, lexer, valid, COMPOUND_OPEN);
752
+ return class_delimiter(s, lexer, valid, COMPOUND_OPEN);
790
753
  if (token == ESCAPE_PREFIX) {
791
754
  s->next = s->prefix_kind;
792
755
  return consume(s, lexer, valid, token, s->prefix_end);
@@ -816,8 +779,7 @@ void tree_sitter_gitattributes_external_scanner_deserialize(
816
779
  unsigned length
817
780
  ) {
818
781
  memset(payload, 0, sizeof(Scanner));
819
- if (length == sizeof(Scanner))
820
- memcpy(payload, buffer, length);
782
+ if (length == sizeof(Scanner)) memcpy(payload, buffer, length);
821
783
  }
822
784
 
823
785
  bool tree_sitter_gitattributes_external_scanner_scan(
@@ -826,8 +788,7 @@ bool tree_sitter_gitattributes_external_scanner_scan(
826
788
  const bool *valid
827
789
  ) {
828
790
  Scanner next = *(Scanner *)payload;
829
- if (!scan(&next, lexer, valid))
830
- return false;
791
+ if (!scan(&next, lexer, valid)) return false;
831
792
  *(Scanner *)payload = next;
832
793
  return true;
833
794
  }