1#[derive(
11 Debug, Clone, Copy, PartialEq, Eq, Default, Hash, serde::Serialize, serde::Deserialize,
12)]
13pub struct FileId(pub u32);
14
15impl FileId {
16 pub const DEFAULT: FileId = FileId(0);
19
20 pub const PRELUDE: FileId = FileId(0xFFFF_FFFE);
24
25 pub const BUILTIN: FileId = FileId(0xFFFF_FFFD);
30
31 #[inline]
33 pub const fn new(id: u32) -> Self {
34 Self(id)
35 }
36
37 #[inline]
39 pub const fn index(self) -> u32 {
40 self.0
41 }
42}
43
44#[derive(
50 Debug, Clone, Copy, PartialEq, Eq, Default, Hash, serde::Serialize, serde::Deserialize,
51)]
52pub struct Span {
53 pub file_id: FileId,
55 pub start: u32,
57 pub end: u32,
59}
60
61impl Span {
62 #[inline]
66 pub const fn new(start: u32, end: u32) -> Self {
67 Self {
68 file_id: FileId::DEFAULT,
69 start,
70 end,
71 }
72 }
73
74 #[inline]
76 pub const fn with_file(file_id: FileId, start: u32, end: u32) -> Self {
77 Self {
78 file_id,
79 start,
80 end,
81 }
82 }
83
84 #[inline]
88 pub const fn point(pos: u32) -> Self {
89 Self {
90 file_id: FileId::DEFAULT,
91 start: pos,
92 end: pos,
93 }
94 }
95
96 #[inline]
98 pub const fn point_in_file(file_id: FileId, pos: u32) -> Self {
99 Self {
100 file_id,
101 start: pos,
102 end: pos,
103 }
104 }
105
106 #[inline]
110 pub const fn cover(a: Span, b: Span) -> Self {
111 Self {
112 file_id: a.file_id,
113 start: if a.start < b.start { a.start } else { b.start },
114 end: if a.end > b.end { a.end } else { b.end },
115 }
116 }
117
118 #[inline]
122 pub const fn extend_to(&self, end: u32) -> Self {
123 Self {
124 file_id: self.file_id,
125 start: self.start,
126 end,
127 }
128 }
129
130 #[inline]
132 pub const fn start(&self) -> u32 {
133 self.start
134 }
135
136 #[inline]
138 pub const fn len(&self) -> u32 {
139 self.end - self.start
140 }
141
142 #[inline]
144 pub const fn is_empty(&self) -> bool {
145 self.start == self.end
146 }
147
148 #[inline]
167 pub const fn contains(&self, other: Span) -> bool {
168 self.start <= other.start && other.end <= self.end
169 }
170
171 #[inline]
188 pub const fn contains_pos(&self, pos: u32) -> bool {
189 self.start <= pos && pos < self.end
190 }
191
192 #[inline]
194 pub const fn as_range(&self) -> std::ops::Range<usize> {
195 self.start as usize..self.end as usize
196 }
197
198 #[inline]
207 pub fn line_number(&self, source: &str) -> usize {
208 debug_assert!(
209 (self.start as usize) <= source.len(),
210 "span start {} exceeds source length {}",
211 self.start,
212 source.len()
213 );
214 byte_offset_to_line(source, self.start as usize)
215 }
216
217 #[inline]
227 pub fn line_col(&self, source: &str) -> (usize, usize) {
228 debug_assert!(
229 (self.start as usize) <= source.len(),
230 "span start {} exceeds source length {}",
231 self.start,
232 source.len()
233 );
234 byte_offset_to_line_col(source, self.start as usize)
235 }
236}
237
238#[inline]
246pub fn byte_offset_to_line(source: &str, offset: usize) -> usize {
247 source[..offset.min(source.len())]
248 .bytes()
249 .filter(|&b| b == b'\n')
250 .count()
251 + 1
252}
253
254#[inline]
263pub fn byte_offset_to_line_col(source: &str, offset: usize) -> (usize, usize) {
264 let offset = offset.min(source.len());
265 let prefix = &source[..offset];
266
267 match prefix.rfind('\n') {
269 Some(newline_pos) => {
270 let line = prefix.bytes().filter(|&b| b == b'\n').count() + 1;
271 let col = offset - newline_pos; (line, col)
273 }
274 None => {
275 (1, offset + 1)
277 }
278 }
279}
280
281#[derive(Debug, Clone)]
299pub struct LineIndex {
300 line_starts: Vec<u32>,
303 source_len: u32,
305}
306
307impl LineIndex {
308 pub fn new(source: &str) -> Self {
313 let mut line_starts = vec![0u32];
314 for (i, byte) in source.bytes().enumerate() {
315 if byte == b'\n' {
316 line_starts.push((i + 1) as u32);
317 }
318 }
319 Self {
320 line_starts,
321 source_len: source.len() as u32,
322 }
323 }
324
325 #[inline]
334 pub fn line_number(&self, offset: u32) -> usize {
335 debug_assert!(
336 offset <= self.source_len,
337 "offset {} exceeds source length {}",
338 offset,
339 self.source_len
340 );
341 let offset = offset.min(self.source_len);
342
343 self.line_starts.partition_point(|&start| start <= offset)
349 }
350
351 #[inline]
353 pub fn span_line_number(&self, span: Span) -> usize {
354 self.line_number(span.start)
355 }
356
357 #[inline]
369 pub fn line_col(&self, offset: u32) -> (usize, usize) {
370 debug_assert!(
371 offset <= self.source_len,
372 "offset {} exceeds source length {}",
373 offset,
374 self.source_len
375 );
376 let offset = offset.min(self.source_len);
377
378 let line_idx = self.line_starts.partition_point(|&start| start <= offset);
380 let line_start = self.line_starts[line_idx - 1];
382 let col = (offset - line_start) as usize + 1;
383 (line_idx, col)
384 }
385
386 #[inline]
390 pub fn span_line_col(&self, span: Span) -> (usize, usize) {
391 self.line_col(span.start)
392 }
393
394 #[inline]
396 pub fn line_count(&self) -> usize {
397 self.line_starts.len()
398 }
399}
400
401impl From<std::ops::Range<usize>> for Span {
402 #[inline]
403 fn from(range: std::ops::Range<usize>) -> Self {
404 Self {
405 file_id: FileId::DEFAULT,
406 start: range.start as u32,
407 end: range.end as u32,
408 }
409 }
410}
411
412impl From<std::ops::Range<u32>> for Span {
413 #[inline]
414 fn from(range: std::ops::Range<u32>) -> Self {
415 Self {
416 file_id: FileId::DEFAULT,
417 start: range.start,
418 end: range.end,
419 }
420 }
421}
422
423#[cfg(test)]
424mod tests {
425 use super::*;
426
427 #[test]
428 fn test_span_size() {
429 assert_eq!(std::mem::size_of::<Span>(), 12);
431 }
432
433 #[test]
434 fn test_file_id() {
435 assert_eq!(FileId::DEFAULT.index(), 0);
436 assert_eq!(FileId::new(42).index(), 42);
437 }
438
439 #[test]
440 fn test_span_with_file() {
441 let file = FileId::new(5);
442 let span = Span::with_file(file, 10, 20);
443 assert_eq!(span.file_id, file);
444 assert_eq!(span.start, 10);
445 assert_eq!(span.end, 20);
446 }
447
448 #[test]
449 fn test_span_point_in_file() {
450 let file = FileId::new(3);
451 let span = Span::point_in_file(file, 15);
452 assert_eq!(span.file_id, file);
453 assert_eq!(span.start, 15);
454 assert_eq!(span.end, 15);
455 }
456
457 #[test]
458 fn test_span_cover_preserves_file_id() {
459 let file = FileId::new(7);
460 let a = Span::with_file(file, 5, 10);
461 let b = Span::with_file(file, 15, 20);
462 let covered = Span::cover(a, b);
463 assert_eq!(covered.file_id, file);
464 assert_eq!(covered.start, 5);
465 assert_eq!(covered.end, 20);
466 }
467
468 #[test]
469 fn test_span_cover() {
470 let a = Span::new(5, 10);
471 let b = Span::new(15, 20);
472 let covered = Span::cover(a, b);
473 assert_eq!(covered, Span::new(5, 20));
474 }
475
476 #[test]
477 fn test_span_from_range() {
478 let span: Span = (5usize..10usize).into();
479 assert_eq!(span.start, 5);
480 assert_eq!(span.end, 10);
481 }
482
483 #[test]
484 fn test_byte_offset_to_line() {
485 let source = "line1\nline2\nline3";
486 assert_eq!(byte_offset_to_line(source, 0), 1);
488 assert_eq!(byte_offset_to_line(source, 4), 1);
489 assert_eq!(byte_offset_to_line(source, 6), 2);
491 assert_eq!(byte_offset_to_line(source, 10), 2);
492 assert_eq!(byte_offset_to_line(source, 12), 3);
494 assert_eq!(byte_offset_to_line(source, 16), 3);
495 }
496
497 #[test]
498 fn test_span_line_number() {
499 let source = "let x = 1;\nlet y = 2;\nlet z = 3;";
500 let span1 = Span::new(0, 10);
502 assert_eq!(span1.line_number(source), 1);
503 let span2 = Span::new(11, 21);
505 assert_eq!(span2.line_number(source), 2);
506 let span3 = Span::new(22, 32);
508 assert_eq!(span3.line_number(source), 3);
509 }
510
511 #[test]
512 fn test_byte_offset_to_line_at_bounds() {
513 let source = "hello";
514 assert_eq!(byte_offset_to_line(source, 5), 1);
516 assert_eq!(byte_offset_to_line(source, 100), 1);
518 }
519
520 #[test]
521 fn test_byte_offset_to_line_empty_source() {
522 let source = "";
523 assert_eq!(byte_offset_to_line(source, 0), 1);
525 }
526
527 #[test]
528 fn test_span_line_number_at_newline() {
529 let source = "a\nb";
530 let span = Span::new(1, 2);
532 assert_eq!(span.line_number(source), 1);
533 let span2 = Span::new(2, 3);
535 assert_eq!(span2.line_number(source), 2);
536 }
537
538 #[test]
543 fn test_line_index_basic() {
544 let source = "line1\nline2\nline3";
545 let index = LineIndex::new(source);
546
547 assert_eq!(index.line_number(0), 1);
549 assert_eq!(index.line_number(4), 1);
550
551 assert_eq!(index.line_number(6), 2);
553 assert_eq!(index.line_number(10), 2);
554
555 assert_eq!(index.line_number(12), 3);
557 assert_eq!(index.line_number(16), 3);
558 }
559
560 #[test]
561 fn test_line_index_matches_byte_offset_to_line() {
562 let source = "let x = 1;\nlet y = 2;\nlet z = 3;";
563 let index = LineIndex::new(source);
564
565 for offset in 0..=source.len() {
567 assert_eq!(
568 index.line_number(offset as u32),
569 byte_offset_to_line(source, offset),
570 "mismatch at offset {}",
571 offset
572 );
573 }
574 }
575
576 #[test]
577 fn test_line_index_empty_source() {
578 let source = "";
579 let index = LineIndex::new(source);
580 assert_eq!(index.line_number(0), 1);
581 assert_eq!(index.line_count(), 1);
582 }
583
584 #[test]
585 fn test_line_index_single_line() {
586 let source = "hello";
587 let index = LineIndex::new(source);
588 assert_eq!(index.line_number(0), 1);
589 assert_eq!(index.line_number(4), 1);
590 assert_eq!(index.line_count(), 1);
591 }
592
593 #[test]
594 fn test_line_index_at_newline() {
595 let source = "a\nb";
596 let index = LineIndex::new(source);
597 assert_eq!(index.line_number(1), 1);
599 assert_eq!(index.line_number(2), 2);
601 }
602
603 #[test]
604 fn test_line_index_trailing_newline() {
605 let source = "line1\n";
606 let index = LineIndex::new(source);
607 assert_eq!(index.line_number(0), 1);
608 assert_eq!(index.line_number(5), 1); assert_eq!(index.line_number(6), 2); assert_eq!(index.line_count(), 2);
611 }
612
613 #[test]
614 fn test_line_index_span_line_number() {
615 let source = "let x = 1;\nlet y = 2;\nlet z = 3;";
616 let index = LineIndex::new(source);
617
618 let span1 = Span::new(0, 10);
619 assert_eq!(index.span_line_number(span1), 1);
620
621 let span2 = Span::new(11, 21);
622 assert_eq!(index.span_line_number(span2), 2);
623
624 let span3 = Span::new(22, 32);
625 assert_eq!(index.span_line_number(span3), 3);
626 }
627
628 #[test]
629 fn test_line_index_at_bounds() {
630 let source = "hello";
631 let index = LineIndex::new(source);
632 assert_eq!(index.line_number(5), 1);
634 }
635
636 #[test]
637 fn test_line_index_line_count() {
638 assert_eq!(LineIndex::new("").line_count(), 1);
639 assert_eq!(LineIndex::new("a").line_count(), 1);
640 assert_eq!(LineIndex::new("a\n").line_count(), 2);
641 assert_eq!(LineIndex::new("a\nb").line_count(), 2);
642 assert_eq!(LineIndex::new("a\nb\n").line_count(), 3);
643 assert_eq!(LineIndex::new("a\nb\nc").line_count(), 3);
644 }
645
646 #[test]
651 fn test_byte_offset_to_line_col_basic() {
652 let source = "line1\nline2\nline3";
653 assert_eq!(byte_offset_to_line_col(source, 0), (1, 1)); assert_eq!(byte_offset_to_line_col(source, 4), (1, 5)); assert_eq!(byte_offset_to_line_col(source, 5), (1, 6)); assert_eq!(byte_offset_to_line_col(source, 6), (2, 1)); assert_eq!(byte_offset_to_line_col(source, 10), (2, 5)); assert_eq!(byte_offset_to_line_col(source, 12), (3, 1)); assert_eq!(byte_offset_to_line_col(source, 16), (3, 5)); }
668
669 #[test]
670 fn test_byte_offset_to_line_col_empty_source() {
671 let source = "";
672 assert_eq!(byte_offset_to_line_col(source, 0), (1, 1));
673 }
674
675 #[test]
676 fn test_byte_offset_to_line_col_single_line() {
677 let source = "hello";
678 assert_eq!(byte_offset_to_line_col(source, 0), (1, 1));
679 assert_eq!(byte_offset_to_line_col(source, 2), (1, 3));
680 assert_eq!(byte_offset_to_line_col(source, 4), (1, 5));
681 assert_eq!(byte_offset_to_line_col(source, 5), (1, 6)); }
683
684 #[test]
685 fn test_byte_offset_to_line_col_at_newline() {
686 let source = "a\nb";
687 assert_eq!(byte_offset_to_line_col(source, 0), (1, 1));
691 assert_eq!(byte_offset_to_line_col(source, 1), (1, 2));
692 assert_eq!(byte_offset_to_line_col(source, 2), (2, 1));
693 }
694
695 #[test]
696 fn test_span_line_col() {
697 let source = "let x = 1;\nlet y = 2;\nlet z = 3;";
698 let span1 = Span::new(0, 10);
703 assert_eq!(span1.line_col(source), (1, 1));
704
705 let span2 = Span::new(11, 21);
706 assert_eq!(span2.line_col(source), (2, 1));
707
708 let span3 = Span::new(22, 32);
709 assert_eq!(span3.line_col(source), (3, 1));
710
711 let span_mid = Span::new(4, 10); assert_eq!(span_mid.line_col(source), (1, 5)); }
715
716 #[test]
717 fn test_line_index_line_col_basic() {
718 let source = "line1\nline2\nline3";
719 let index = LineIndex::new(source);
720
721 assert_eq!(index.line_col(0), (1, 1));
723 assert_eq!(index.line_col(4), (1, 5));
724
725 assert_eq!(index.line_col(6), (2, 1));
727 assert_eq!(index.line_col(10), (2, 5));
728
729 assert_eq!(index.line_col(12), (3, 1));
731 assert_eq!(index.line_col(16), (3, 5));
732 }
733
734 #[test]
735 fn test_line_index_line_col_matches_byte_offset() {
736 let source = "let x = 1;\nlet y = 2;\nlet z = 3;";
737 let index = LineIndex::new(source);
738
739 for offset in 0..=source.len() {
741 assert_eq!(
742 index.line_col(offset as u32),
743 byte_offset_to_line_col(source, offset),
744 "mismatch at offset {}",
745 offset
746 );
747 }
748 }
749
750 #[test]
751 fn test_line_index_span_line_col() {
752 let source = "let x = 1;\nlet y = 2;\nlet z = 3;";
753 let index = LineIndex::new(source);
754
755 let span1 = Span::new(0, 10);
756 assert_eq!(index.span_line_col(span1), (1, 1));
757
758 let span2 = Span::new(11, 21);
759 assert_eq!(index.span_line_col(span2), (2, 1));
760
761 let span3 = Span::new(22, 32);
762 assert_eq!(index.span_line_col(span3), (3, 1));
763
764 let span_mid = Span::new(4, 10);
766 assert_eq!(index.span_line_col(span_mid), (1, 5));
767 }
768
769 #[test]
770 fn test_line_index_line_col_empty_source() {
771 let source = "";
772 let index = LineIndex::new(source);
773 assert_eq!(index.line_col(0), (1, 1));
774 }
775
776 #[test]
777 fn test_line_index_line_col_at_newline() {
778 let source = "a\nb";
779 let index = LineIndex::new(source);
780 assert_eq!(index.line_col(0), (1, 1)); assert_eq!(index.line_col(1), (1, 2)); assert_eq!(index.line_col(2), (2, 1)); }
784
785 #[test]
790 fn test_span_contains_span() {
791 let outer = Span::new(5, 20);
792
793 assert!(outer.contains(Span::new(5, 20))); assert!(outer.contains(Span::new(5, 10))); assert!(outer.contains(Span::new(15, 20))); assert!(outer.contains(Span::new(10, 15))); assert!(!outer.contains(Span::new(0, 5))); assert!(!outer.contains(Span::new(0, 10))); assert!(!outer.contains(Span::new(15, 25))); assert!(!outer.contains(Span::new(20, 25))); assert!(!outer.contains(Span::new(0, 25))); }
806
807 #[test]
808 fn test_span_contains_point() {
809 let outer = Span::new(5, 20);
810
811 assert!(outer.contains(Span::point(5))); assert!(outer.contains(Span::point(10))); assert!(outer.contains(Span::point(20))); assert!(!outer.contains(Span::point(4))); assert!(!outer.contains(Span::point(21))); }
820
821 #[test]
822 fn test_span_contains_empty_span() {
823 let empty = Span::point(10);
824
825 assert!(empty.contains(Span::point(10)));
827 assert!(!empty.contains(Span::point(9)));
828 assert!(!empty.contains(Span::new(10, 11)));
829 }
830
831 #[test]
832 fn test_span_contains_pos() {
833 let span = Span::new(5, 10);
834
835 assert!(!span.contains_pos(0));
837 assert!(!span.contains_pos(4));
838
839 assert!(span.contains_pos(5)); assert!(span.contains_pos(7)); assert!(span.contains_pos(9)); assert!(!span.contains_pos(10)); assert!(!span.contains_pos(11));
847 assert!(!span.contains_pos(100));
848 }
849
850 #[test]
851 fn test_span_contains_pos_empty_span() {
852 let empty = Span::point(10);
853
854 assert!(!empty.contains_pos(9));
856 assert!(!empty.contains_pos(10));
857 assert!(!empty.contains_pos(11));
858 }
859}