@konomanoasa/tree-sitter-gitattributes 0.2.0 → 0.3.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
@@ -9,7 +9,6 @@ enum Token {
9
9
  RULE_START,
10
10
  MACRO_START,
11
11
  END_OF_FILE,
12
- LINE_ENDING,
13
12
  LAYOUT,
14
13
  SEPARATOR,
15
14
  ATTRIBUTE_START,
@@ -66,35 +65,33 @@ enum Token {
66
65
  MISSING_NAME,
67
66
  INCOMPLETE_NAME,
68
67
  INVALID_NAME_CHARACTER,
68
+ INVALID_NAME_ESCAPE,
69
69
  ERROR_SENTINEL
70
70
  };
71
71
 
72
72
  #define NONE UINT32_MAX
73
73
 
74
- // A bracket item: text, an escape, or a class term such as `[:alpha:]`.
74
+ enum { DECODE_ERROR = -1, CURSOR_END = -2, INVALID_UNIT = -3 };
75
+
75
76
  typedef struct {
76
77
  uint32_t kind, end, opening_end, closing_start;
77
78
  } Item;
78
79
 
79
- // Positions count source characters from the current line start. Quoted units
80
- // are characters or C escapes; ranges stay on the original source. Zero-width
81
- // start tokens save lookahead decisions before their source is consumed.
80
+ // Positions are line-relative character offsets; uint32_t avoids padding.
82
81
  typedef struct {
83
82
  uint32_t position, leading, line_end, content_start, content_end, kind;
84
83
  uint32_t token_end, name_start, name_end;
85
84
  uint32_t next, next_end, prefix_end, prefix_kind;
86
85
  uint32_t set_end, set_close_end, class_start, range_operator_end;
87
86
  Item class, lower, upper;
88
- bool quoted, component_start, after_separator;
87
+ uint32_t quoted, component_start, after_separator;
89
88
  } Scanner;
90
89
 
91
90
  typedef char scanner_fits_buffer
92
91
  [sizeof(Scanner) <= TREE_SITTER_SERIALIZATION_BUFFER_SIZE ? 1 : -1];
93
92
 
94
- // Negative c values: -1 decode failure, -2 cursor limit, -3 invalid or
95
- // incomplete escape.
96
93
  typedef struct {
97
- Scanner *s;
94
+ const Scanner *s;
98
95
  TSLexer *lexer;
99
96
  uint32_t raw, limit, start, end, kind;
100
97
  int32_t c;
@@ -138,9 +135,18 @@ static bool consume(
138
135
  enum Token token,
139
136
  uint32_t end
140
137
  ) {
141
- while (s->position < end)
142
- advance(s, lexer);
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
+ }
143
146
  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);
144
150
  return emit(lexer, valid, token);
145
151
  }
146
152
 
@@ -151,9 +157,9 @@ static bool text_run(
151
157
  enum Token token,
152
158
  uint32_t end
153
159
  ) {
154
- if (lexer->lookahead == -1)
155
- return consume(s, lexer, valid, INVALID_ENCODING, s->position + 1);
156
- while (s->position < end && lexer->lookahead != -1)
160
+ if (lexer->lookahead == DECODE_ERROR)
161
+ return consume(s, lexer, valid, INVALID_ENCODING, end);
162
+ while (s->position < end && lexer->lookahead != DECODE_ERROR)
157
163
  advance(s, lexer);
158
164
  lexer->mark_end(lexer);
159
165
  return emit(lexer, valid, token);
@@ -164,42 +170,43 @@ static void raw_step(Cursor *r) {
164
170
  r->raw++;
165
171
  }
166
172
 
167
- // Quoted units decode named C escapes or three octal digits starting with 0
168
- // to 3.
169
173
  static void step(Cursor *r) {
170
174
  r->start = r->end = r->raw;
171
175
  r->kind = 0;
172
- r->c = -2;
176
+ r->c = CURSOR_END;
173
177
  if (r->raw >= r->limit)
174
178
  return;
175
179
  int32_t c = r->lexer->lookahead;
176
180
  raw_step(r);
177
181
  r->c = c;
178
- if (c == -1)
182
+ if (c == DECODE_ERROR) {
179
183
  r->kind = INVALID_ENCODING;
180
- if (r->s->quoted && r->start < r->s->content_end && c == '\\') {
184
+ while (r->raw < r->limit && r->lexer->lookahead == DECODE_ERROR)
185
+ raw_step(r);
186
+ } else if (r->s->quoted && r->start < r->s->content_end && c == '\\') {
181
187
  r->kind = QUOTED_ESCAPE;
182
188
  c = r->lexer->lookahead;
183
189
  if (r->raw >= r->limit) {
184
- r->c = -3;
190
+ r->c = INVALID_UNIT;
185
191
  r->kind = absent(r->lexer, ENDED_QUOTED_ESCAPE, INCOMPLETE_QUOTED_ESCAPE);
186
- } else if (c == -1) {
187
- r->c = -3;
192
+ } else if (c == DECODE_ERROR) {
193
+ r->c = INVALID_UNIT;
188
194
  r->kind = ESCAPE_PREFIX;
189
195
  } else if (c >= '0' && c <= '3') {
190
196
  int32_t value = 0;
191
197
  unsigned digits = 0;
192
198
  while (digits < 3 && r->raw < r->limit) {
193
- int32_t digit = r->lexer->lookahead;
199
+ const int32_t digit = r->lexer->lookahead;
194
200
  if (digit < '0' || digit > '7')
195
201
  break;
196
202
  value = value * 8 + digit - '0';
197
203
  raw_step(r);
198
204
  digits++;
199
205
  }
200
- r->c = value;
201
- if (digits != 3) {
202
- r->c = -3;
206
+ if (digits == 3)
207
+ r->c = value;
208
+ else {
209
+ r->c = INVALID_UNIT;
203
210
  r->kind = r->raw == r->limit
204
211
  ? absent(r->lexer, ENDED_QUOTED_ESCAPE, INCOMPLETE_QUOTED_ESCAPE)
205
212
  : ENDED_QUOTED_ESCAPE;
@@ -233,7 +240,7 @@ static void step(Cursor *r) {
233
240
  r->c = c;
234
241
  break;
235
242
  default:
236
- r->c = -3;
243
+ r->c = INVALID_UNIT;
237
244
  r->kind = INVALID_QUOTED_ESCAPE;
238
245
  break;
239
246
  }
@@ -242,66 +249,62 @@ static void step(Cursor *r) {
242
249
  r->end = r->raw;
243
250
  }
244
251
 
245
- static Cursor cursor(Scanner *s, TSLexer *lexer, uint32_t limit) {
252
+ static Cursor cursor(const Scanner *s, TSLexer *lexer, uint32_t limit) {
246
253
  Cursor r = {.s = s, .lexer = lexer, .raw = s->position, .limit = limit};
247
254
  step(&r);
248
255
  return r;
249
256
  }
250
257
 
251
- // An unclosed quote leaves the pattern content ending at the line end.
252
258
  static bool start_line(Scanner *s, TSLexer *lexer, const bool *valid) {
253
259
  if (lexer->eof(lexer))
254
260
  return false;
255
- Scanner line;
256
- memset(&line, 0, sizeof(line));
261
+ memset(s, 0, sizeof(*s));
262
+ s->class_start = NONE;
263
+ s->component_start = true;
257
264
  int32_t last = 0;
258
265
  lexer->mark_end(lexer);
259
266
  while (!lexer->eof(lexer) && whitespace(lexer->lookahead)) {
260
267
  last = lexer->lookahead;
261
- advance(&line, lexer);
268
+ advance(s, lexer);
262
269
  }
263
- line.leading = line.position;
264
- line.kind = lexer->lookahead == '#' ? COMMENT_START : RULE_START;
265
- line.quoted = lexer->lookahead == '"';
266
- line.content_start = line.leading + (line.quoted ? 1 : 0);
267
- line.content_end = NONE;
270
+ s->leading = s->position;
271
+ s->kind = lexer->lookahead == '#' ? COMMENT_START : RULE_START;
272
+ s->quoted = lexer->lookahead == '"';
273
+ s->content_start = s->leading + (s->quoted ? 1 : 0);
274
+ s->content_end = NONE;
268
275
  bool escaped = false;
269
276
  while (!lexer->eof(lexer) && lexer->lookahead != '\n') {
270
- int32_t c = lexer->lookahead;
271
- if (line.position >= line.content_start && line.content_end == NONE) {
272
- if (line.quoted) {
277
+ const int32_t c = lexer->lookahead;
278
+ if (s->position >= s->content_start && s->content_end == NONE) {
279
+ if (s->quoted) {
273
280
  if (c == '"' && !escaped)
274
- line.content_end = line.position;
281
+ s->content_end = s->position;
275
282
  escaped = c == '\\' && !escaped;
276
283
  } else if (whitespace(c))
277
- line.content_end = line.position;
284
+ s->content_end = s->position;
278
285
  }
279
286
  last = c;
280
- advance(&line, lexer);
287
+ advance(s, lexer);
281
288
  }
282
- line.line_end = line.position;
289
+ s->line_end = s->position;
283
290
  if (last == '\r' && lexer->lookahead == '\n')
284
- line.line_end--;
285
- // The CR of a CRLF ending is neither leading whitespace nor content.
286
- if (line.leading > line.line_end)
287
- line.leading = line.line_end;
288
- if (line.content_end == NONE || line.content_end > line.line_end)
289
- line.content_end = line.line_end;
290
- if (line.leading >= line.line_end)
291
- line.kind = BLANK_START;
292
- line.position = 0;
293
- line.class_start = NONE;
294
- line.component_start = true;
295
- *s = line;
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;
298
+ s->position = 0;
296
299
  return emit(lexer, valid, LINE_START);
297
300
  }
298
301
 
299
- // Compare the macro marker after decoding C escapes.
300
302
  static bool start_content(Scanner *s, TSLexer *lexer, const bool *valid) {
301
303
  while (s->position < s->leading)
302
304
  advance(s, lexer);
303
305
  lexer->mark_end(lexer);
304
306
  if (s->kind == RULE_START) {
307
+ // The opening quote is lookahead; QUOTE_OPEN consumes it later.
305
308
  if (s->quoted)
306
309
  lexer->advance(lexer, false);
307
310
  Cursor r = {
@@ -336,16 +339,12 @@ static void skip_escaped(Cursor *r, Cursor *last, uint32_t *candidate) {
336
339
  }
337
340
 
338
341
  // Escapes and range upper endpoints take precedence over class candidates.
339
- // A candidate starts at [: and ends at the first unescaped ], preceded by a
340
- // colon other than the opening one. An escaped ] cancels it; candidate units
341
- // remain ordinary items. `lower` records an available lower endpoint.
342
- // Success leaves the cursor after the class; failure at ] or the limit.
343
342
  static bool next_class(Cursor *r, bool lower, uint32_t *start, Item *class) {
344
343
  Item a = {CLASS_OPEN, 0, 0, 0};
345
344
  uint32_t candidate = NONE;
346
345
  Cursor last = *r;
347
- while (r->c != -2) {
348
- int32_t here = r->c;
346
+ while (r->c != CURSOR_END) {
347
+ const int32_t here = r->c;
349
348
  if (here == ']') {
350
349
  if (candidate == NONE || last.c != ':' || last.end == a.opening_end)
351
350
  return false;
@@ -360,14 +359,14 @@ static bool next_class(Cursor *r, bool lower, uint32_t *start, Item *class) {
360
359
  step(r);
361
360
  if (here == '\\') {
362
361
  lower = r->c >= 0;
363
- if (r->c != -2)
362
+ if (r->c != CURSOR_END)
364
363
  skip_escaped(r, &last, &candidate);
365
364
  continue;
366
365
  }
367
- if (here == '-' && lower && r->c != -2 && r->c != ']') {
368
- int32_t upper = r->c;
366
+ if (here == '-' && lower && r->c != CURSOR_END && r->c != ']') {
367
+ const int32_t upper = r->c;
369
368
  skip_escaped(r, &last, &candidate);
370
- if (upper == '\\' && r->c != -2)
369
+ if (upper == '\\' && r->c != CURSOR_END)
371
370
  skip_escaped(r, &last, &candidate);
372
371
  lower = false;
373
372
  continue;
@@ -381,7 +380,6 @@ static bool next_class(Cursor *r, bool lower, uint32_t *start, Item *class) {
381
380
  return false;
382
381
  }
383
382
 
384
- // The cursor must be after the opening bracket.
385
383
  static void find_set(Scanner *s, Cursor *r) {
386
384
  s->set_end = s->set_close_end = s->content_end;
387
385
  s->class_start = NONE;
@@ -407,13 +405,13 @@ static void find_set(Scanner *s, Cursor *r) {
407
405
 
408
406
  static Item item(Cursor *r) {
409
407
  Item a = {SET_TEXT, 0, 0, 0};
410
- if (r->c == -2)
408
+ if (r->c == CURSOR_END)
411
409
  return a;
412
- int32_t c = r->c;
413
- uint32_t end = r->end;
410
+ const int32_t c = r->c;
411
+ const uint32_t end = r->end;
414
412
  if (c == '[' && r->start == r->s->class_start) {
415
413
  a = r->s->class;
416
- while (r->c != -2 && r->start < a.end)
414
+ while (r->c != CURSOR_END && r->start < a.end)
417
415
  step(r);
418
416
  return a;
419
417
  }
@@ -421,7 +419,7 @@ static Item item(Cursor *r) {
421
419
  a.kind = r->kind;
422
420
  step(r);
423
421
  if (c == '\\') {
424
- if (r->c == -2)
422
+ if (r->c == CURSOR_END)
425
423
  a.kind = absent(r->lexer, INVALID_ESCAPE, INCOMPLETE_ESCAPE);
426
424
  else if (r->c < 0)
427
425
  a.kind = ESCAPE_PREFIX;
@@ -441,20 +439,14 @@ static bool endpoint(Item a) {
441
439
  (a.kind == SET_TEXT || a.kind == ESCAPE || a.kind == QUOTED_ESCAPE);
442
440
  }
443
441
 
444
- static bool glob_special(int32_t c) {
445
- return c == '\\' || c == '*' || c == '?' || c == '/';
446
- }
447
-
448
- // End literal runs before [ so the set can be decided.
449
- static bool special(const Cursor *r) {
442
+ static bool literal_boundary(const Cursor *r) {
450
443
  if (r->c < 0 || r->kind == QUOTED_ESCAPE)
451
444
  return true;
452
- return glob_special(r->c) || r->c == '[';
445
+ const int32_t c = r->c;
446
+ return c == '\\' || c == '*' || c == '?' || c == '/' || c == '[';
453
447
  }
454
448
 
455
- // A backslash followed by a unit that cannot be escaped is held by the owner
456
- // up to `prefix_end`, and the unit's own issue follows. A C escape prefix
457
- // before an undecodable byte extends the held prefix to that byte.
449
+ // Keep the backslash in its owner; report the following unit's own issue.
458
450
  static void hold_prefix(Scanner *s, Cursor *r, uint32_t prefix_end) {
459
451
  s->next = ESCAPE_PREFIX;
460
452
  s->prefix_end = prefix_end;
@@ -467,12 +459,12 @@ static void hold_prefix(Scanner *s, Cursor *r, uint32_t prefix_end) {
467
459
  s->next_end = r->end;
468
460
  }
469
461
 
470
- // Advance through every lookahead unit so edits inside it invalidate the
471
- // decision. Nonliteral tokens are decided here and consumed by the next call.
462
+ // Consume lookahead units fully so edits inside them invalidate the decision.
472
463
  static bool prepare_glob(Scanner *s, TSLexer *lexer, const bool *valid) {
473
464
  lexer->mark_end(lexer);
474
465
  Cursor r = cursor(s, lexer, s->content_end);
475
- uint32_t start = s->position;
466
+ bool separator = r.c == '/';
467
+ const uint32_t start = s->position;
476
468
  s->next_end = r.end;
477
469
  if (r.c < 0) {
478
470
  if (r.kind == ESCAPE_PREFIX)
@@ -487,13 +479,14 @@ static bool prepare_glob(Scanner *s, TSLexer *lexer, const bool *valid) {
487
479
  s->next = PATH_SEPARATOR;
488
480
  else if (r.c == '\\') {
489
481
  step(&r);
490
- if (r.c == -2)
482
+ if (r.c == CURSOR_END)
491
483
  s->next = absent(lexer, INVALID_ESCAPE, INCOMPLETE_ESCAPE);
492
484
  else if (r.c < 0)
493
485
  hold_prefix(s, &r, s->next_end);
494
486
  else {
495
487
  s->next = ESCAPE;
496
488
  s->next_end = r.end;
489
+ separator = r.c == '/';
497
490
  }
498
491
  } else if (r.c == '*') {
499
492
  unsigned count = 0;
@@ -502,9 +495,11 @@ static bool prepare_glob(Scanner *s, TSLexer *lexer, const bool *valid) {
502
495
  s->next_end = r.end;
503
496
  step(&r);
504
497
  }
505
- bool pair = count == 2 && s->component_start;
506
- bool slash = r.c == '/';
507
- bool tail = r.c == -2 && s->after_separator;
498
+ const bool pair = count == 2 && s->component_start;
499
+ const bool tail = r.c == CURSOR_END && s->after_separator;
500
+ if (r.c == '\\')
501
+ step(&r);
502
+ const bool slash = r.c == '/';
508
503
  s->next = pair && (slash || tail) ? RECURSIVE_WILDCARD : WILDCARD;
509
504
  } else if (r.kind == QUOTED_ESCAPE && r.c != '[')
510
505
  s->next = QUOTED_ESCAPE;
@@ -516,7 +511,7 @@ static bool prepare_glob(Scanner *s, TSLexer *lexer, const bool *valid) {
516
511
  uint32_t end = r.end;
517
512
  lexer->mark_end(lexer);
518
513
  step(&r);
519
- while (!special(&r)) {
514
+ while (!literal_boundary(&r)) {
520
515
  end = r.end;
521
516
  lexer->mark_end(lexer);
522
517
  step(&r);
@@ -525,13 +520,16 @@ static bool prepare_glob(Scanner *s, TSLexer *lexer, const bool *valid) {
525
520
  s->component_start = false;
526
521
  return emit(lexer, valid, LITERAL);
527
522
  }
523
+ s->component_start = separator || s->next == NEGATIVE_PATTERN;
524
+ if (separator)
525
+ s->after_separator = true;
528
526
  return emit(lexer, valid, GLOB_START);
529
527
  }
530
528
 
531
529
  static bool prepare_set(Scanner *s, TSLexer *lexer, const bool *valid) {
532
530
  lexer->mark_end(lexer);
533
531
  Cursor r = cursor(s, lexer, s->set_end);
534
- uint32_t start = s->position;
532
+ const uint32_t start = s->position;
535
533
  if (valid[SET_NEGATION] && (r.c == '!' || r.c == '^')) {
536
534
  s->position = r.end;
537
535
  lexer->mark_end(lexer);
@@ -543,10 +541,10 @@ static bool prepare_set(Scanner *s, TSLexer *lexer, const bool *valid) {
543
541
  Item current = item(&r);
544
542
  for (;;) {
545
543
  if (endpoint(current) && r.c == '-' && r.end < s->set_end) {
546
- uint32_t operator_end = r.end;
547
- bool plain_operator = r.kind != QUOTED_ESCAPE;
544
+ const uint32_t operator_end = r.end;
545
+ const bool plain_operator = r.kind != QUOTED_ESCAPE;
548
546
  step(&r);
549
- Item upper = item(&r);
547
+ const Item upper = item(&r);
550
548
  if (endpoint(upper)) {
551
549
  if (here == start) {
552
550
  s->next = RANGE_START;
@@ -558,8 +556,6 @@ static bool prepare_set(Scanner *s, TSLexer *lexer, const bool *valid) {
558
556
  break;
559
557
  }
560
558
  if (current.kind == SET_TEXT) {
561
- // A literal hyphen joins the text run; an escaped one stays a
562
- // separate `quoted_escape`.
563
559
  if (!plain_operator) {
564
560
  s->next_end = current.end;
565
561
  break;
@@ -582,7 +578,7 @@ static bool prepare_set(Scanner *s, TSLexer *lexer, const bool *valid) {
582
578
  }
583
579
  s->next_end = current.end;
584
580
  here = r.start;
585
- if (r.c == -2)
581
+ if (r.c == CURSOR_END)
586
582
  break;
587
583
  current = item(&r);
588
584
  }
@@ -597,7 +593,6 @@ unit_token(Scanner *s, TSLexer *lexer, const bool *valid, enum Token token) {
597
593
  return emit(lexer, valid, token);
598
594
  }
599
595
 
600
- // Closing a class term must find the next class in the set.
601
596
  static bool class_piece(Scanner *s, TSLexer *lexer, const bool *valid) {
602
597
  if (s->position < s->class.opening_end)
603
598
  return consume(s, lexer, valid, CLASS_OPEN, s->class.opening_end);
@@ -612,8 +607,10 @@ static bool class_piece(Scanner *s, TSLexer *lexer, const bool *valid) {
612
607
  s->class_start = NONE;
613
608
  return emit(lexer, valid, COMPOUND_CLOSE);
614
609
  }
610
+ if (lexer->lookahead == DECODE_ERROR)
611
+ return consume(s, lexer, valid, INVALID_ENCODING, s->class.closing_start);
615
612
  Cursor r = cursor(s, lexer, s->class.closing_start);
616
- enum Token token = r.kind ? (enum Token)r.kind : CLASS_NAME;
613
+ const enum Token token = r.kind ? (enum Token)r.kind : CLASS_NAME;
617
614
  uint32_t end = r.end;
618
615
  lexer->mark_end(lexer);
619
616
  if (token == CLASS_NAME) {
@@ -629,23 +626,26 @@ static bool class_piece(Scanner *s, TSLexer *lexer, const bool *valid) {
629
626
  }
630
627
 
631
628
  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);
632
631
  Cursor r = cursor(s, lexer, s->name_end);
633
632
  enum Token token;
634
- if (r.kind == ESCAPE_PREFIX) {
633
+ if (r.kind == ESCAPE_PREFIX)
635
634
  s->next = INVALID_ENCODING;
636
- s->next_end = r.end + 1;
637
- }
638
635
  if (r.c < 0)
639
636
  token = (enum Token)r.kind;
640
637
  else if (!name_character(r.c, s->position == s->name_start))
641
- token = INVALID_NAME_CHARACTER;
638
+ token =
639
+ r.kind == QUOTED_ESCAPE ? INVALID_NAME_ESCAPE : INVALID_NAME_CHARACTER;
642
640
  else
643
641
  token = r.kind == QUOTED_ESCAPE ? QUOTED_ESCAPE : NAME_TEXT;
644
642
  uint32_t end = r.end;
645
643
  lexer->mark_end(lexer);
646
- if (token == NAME_TEXT) {
644
+ if (token == NAME_TEXT || token == INVALID_NAME_CHARACTER) {
647
645
  step(&r);
648
- while (r.c >= 0 && !r.kind && name_character(r.c, false)) {
646
+ while (
647
+ r.c >= 0 && !r.kind && name_character(r.c, false) == (token == NAME_TEXT)
648
+ ) {
649
649
  end = r.end;
650
650
  lexer->mark_end(lexer);
651
651
  step(&r);
@@ -657,19 +657,18 @@ static bool name_piece(Scanner *s, TSLexer *lexer, const bool *valid) {
657
657
 
658
658
  static bool prepare_attribute(Scanner *s, TSLexer *lexer, const bool *valid) {
659
659
  lexer->mark_end(lexer);
660
- uint32_t position = s->position;
661
- bool modified = lexer->lookahead == '-' || lexer->lookahead == '!';
660
+ Cursor r = cursor(s, lexer, s->line_end);
661
+ const bool modified = r.c == '-' || r.c == '!';
662
662
  s->name_start = s->position + (modified ? 1 : 0);
663
663
  s->name_end = NONE;
664
- while (position < s->line_end && !whitespace(lexer->lookahead)) {
665
- if (lexer->lookahead == '=' && s->name_end == NONE)
666
- s->name_end = position;
667
- lexer->advance(lexer, false);
668
- position++;
664
+ while (r.c != CURSOR_END && !whitespace(r.c)) {
665
+ if (r.c == '=' && s->name_end == NONE)
666
+ s->name_end = r.start;
667
+ step(&r);
669
668
  }
670
- s->token_end = position;
669
+ s->token_end = r.start;
671
670
  if (s->name_end == NONE)
672
- s->name_end = position;
671
+ s->name_end = r.start;
673
672
  return emit(lexer, valid, ATTRIBUTE_START);
674
673
  }
675
674
 
@@ -704,7 +703,8 @@ static bool scan(Scanner *s, TSLexer *lexer, const bool *valid) {
704
703
  return prepare_attribute(s, lexer, valid);
705
704
  if (valid[ATTRIBUTE_END] && s->position == s->token_end)
706
705
  return emit(lexer, valid, ATTRIBUTE_END);
707
- bool at_name = s->position == s->name_start && s->position < s->name_end;
706
+ const bool at_name =
707
+ s->position == s->name_start && s->position < s->name_end;
708
708
  if (valid[NAME_START] && at_name)
709
709
  return emit(lexer, valid, NAME_START);
710
710
  if (valid[NAME_END] && s->position == s->name_end)
@@ -726,17 +726,8 @@ static bool scan(Scanner *s, TSLexer *lexer, const bool *valid) {
726
726
  }
727
727
  if (valid[PATTERN_END] && s->position >= s->content_end)
728
728
  return emit(lexer, valid, PATTERN_END);
729
- if (s->position >= s->line_end) {
730
- if (lexer->eof(lexer))
731
- return emit(lexer, valid, END_OF_FILE);
732
- if (lexer->lookahead == '\r')
733
- advance(s, lexer);
734
- if (lexer->lookahead != '\n')
735
- return false;
736
- advance(s, lexer);
737
- lexer->mark_end(lexer);
738
- return emit(lexer, valid, LINE_ENDING);
739
- }
729
+ if (s->position >= s->line_end)
730
+ return lexer->eof(lexer) && emit(lexer, valid, END_OF_FILE);
740
731
  if (s->kind == COMMENT_START) {
741
732
  if (s->position == s->leading)
742
733
  return consume(s, lexer, valid, COMMENT_MARKER, s->position + 1);
@@ -784,7 +775,7 @@ static bool scan(Scanner *s, TSLexer *lexer, const bool *valid) {
784
775
  if (s->upper.end > s->position) {
785
776
  if (s->position == s->lower.end)
786
777
  return consume(s, lexer, valid, RANGE_OPERATOR, s->range_operator_end);
787
- Item a = s->position < s->lower.end ? s->lower : s->upper;
778
+ const Item a = s->position < s->lower.end ? s->lower : s->upper;
788
779
  return consume(
789
780
  s,
790
781
  lexer,
@@ -793,25 +784,24 @@ static bool scan(Scanner *s, TSLexer *lexer, const bool *valid) {
793
784
  a.end
794
785
  );
795
786
  }
796
- enum Token token = (enum Token)s->next;
787
+ const enum Token token = (enum Token)s->next;
797
788
  if (token == CLASS_OPEN)
798
789
  return unit_token(s, lexer, valid, COMPOUND_OPEN);
799
790
  if (token == ESCAPE_PREFIX) {
800
791
  s->next = s->prefix_kind;
801
792
  return consume(s, lexer, valid, token, s->prefix_end);
802
793
  }
803
- s->component_start = token == PATH_SEPARATOR || token == NEGATIVE_PATTERN;
804
- if (token == PATH_SEPARATOR)
805
- s->after_separator = true;
806
794
  return consume(s, lexer, valid, token, s->next_end);
807
795
  }
808
796
 
809
797
  void *tree_sitter_gitattributes_external_scanner_create(void) {
810
798
  return ts_calloc(1, sizeof(Scanner));
811
799
  }
800
+
812
801
  void tree_sitter_gitattributes_external_scanner_destroy(void *payload) {
813
802
  ts_free(payload);
814
803
  }
804
+
815
805
  unsigned tree_sitter_gitattributes_external_scanner_serialize(
816
806
  void *payload,
817
807
  char *buffer
@@ -819,6 +809,7 @@ unsigned tree_sitter_gitattributes_external_scanner_serialize(
819
809
  memcpy(buffer, payload, sizeof(Scanner));
820
810
  return sizeof(Scanner);
821
811
  }
812
+
822
813
  void tree_sitter_gitattributes_external_scanner_deserialize(
823
814
  void *payload,
824
815
  const char *buffer,
@@ -828,6 +819,7 @@ void tree_sitter_gitattributes_external_scanner_deserialize(
828
819
  if (length == sizeof(Scanner))
829
820
  memcpy(payload, buffer, length);
830
821
  }
822
+
831
823
  bool tree_sitter_gitattributes_external_scanner_scan(
832
824
  void *payload,
833
825
  TSLexer *lexer,
package/tree-sitter.json CHANGED
@@ -12,7 +12,7 @@
12
12
  }
13
13
  ],
14
14
  "metadata": {
15
- "version": "0.2.0",
15
+ "version": "0.3.0",
16
16
  "license": "MIT",
17
17
  "description": "Tree-sitter grammar for gitattributes.",
18
18
  "links": {