1#[derive(Debug, Clone, Copy, PartialEq, Eq, Default, Hash)]
11pub struct FileId(pub u32);
12
13impl FileId {
14 pub const DEFAULT: FileId = FileId(0);
17
18 #[inline]
20 pub const fn new(id: u32) -> Self {
21 Self(id)
22 }
23
24 #[inline]
26 pub const fn index(self) -> u32 {
27 self.0
28 }
29}
30
31#[derive(Debug, Clone, Copy, PartialEq, Eq, Default, Hash)]
37pub struct Span {
38 pub file_id: FileId,
40 pub start: u32,
42 pub end: u32,
44}
45
46impl Span {
47 #[inline]
51 pub const fn new(start: u32, end: u32) -> Self {
52 Self {
53 file_id: FileId::DEFAULT,
54 start,
55 end,
56 }
57 }
58
59 #[inline]
61 pub const fn with_file(file_id: FileId, start: u32, end: u32) -> Self {
62 Self {
63 file_id,
64 start,
65 end,
66 }
67 }
68
69 #[inline]
73 pub const fn point(pos: u32) -> Self {
74 Self {
75 file_id: FileId::DEFAULT,
76 start: pos,
77 end: pos,
78 }
79 }
80
81 #[inline]
83 pub const fn point_in_file(file_id: FileId, pos: u32) -> Self {
84 Self {
85 file_id,
86 start: pos,
87 end: pos,
88 }
89 }
90
91 #[inline]
95 pub const fn cover(a: Span, b: Span) -> Self {
96 Self {
97 file_id: a.file_id,
98 start: if a.start < b.start { a.start } else { b.start },
99 end: if a.end > b.end { a.end } else { b.end },
100 }
101 }
102
103 #[inline]
107 pub const fn extend_to(&self, end: u32) -> Self {
108 Self {
109 file_id: self.file_id,
110 start: self.start,
111 end,
112 }
113 }
114
115 #[inline]
117 pub const fn start(&self) -> u32 {
118 self.start
119 }
120
121 #[inline]
123 pub const fn len(&self) -> u32 {
124 self.end - self.start
125 }
126
127 #[inline]
129 pub const fn is_empty(&self) -> bool {
130 self.start == self.end
131 }
132
133 #[inline]
152 pub const fn contains(&self, other: Span) -> bool {
153 self.start <= other.start && other.end <= self.end
154 }
155
156 #[inline]
173 pub const fn contains_pos(&self, pos: u32) -> bool {
174 self.start <= pos && pos < self.end
175 }
176
177 #[inline]
179 pub const fn as_range(&self) -> std::ops::Range<usize> {
180 self.start as usize..self.end as usize
181 }
182
183 #[inline]
192 pub fn line_number(&self, source: &str) -> usize {
193 debug_assert!(
194 (self.start as usize) <= source.len(),
195 "span start {} exceeds source length {}",
196 self.start,
197 source.len()
198 );
199 byte_offset_to_line(source, self.start as usize)
200 }
201
202 #[inline]
212 pub fn line_col(&self, source: &str) -> (usize, usize) {
213 debug_assert!(
214 (self.start as usize) <= source.len(),
215 "span start {} exceeds source length {}",
216 self.start,
217 source.len()
218 );
219 byte_offset_to_line_col(source, self.start as usize)
220 }
221}
222
223#[inline]
231pub fn byte_offset_to_line(source: &str, offset: usize) -> usize {
232 source[..offset.min(source.len())]
233 .bytes()
234 .filter(|&b| b == b'\n')
235 .count()
236 + 1
237}
238
239#[inline]
248pub fn byte_offset_to_line_col(source: &str, offset: usize) -> (usize, usize) {
249 let offset = offset.min(source.len());
250 let prefix = &source[..offset];
251
252 match prefix.rfind('\n') {
254 Some(newline_pos) => {
255 let line = prefix.bytes().filter(|&b| b == b'\n').count() + 1;
256 let col = offset - newline_pos; (line, col)
258 }
259 None => {
260 (1, offset + 1)
262 }
263 }
264}
265
266#[derive(Debug, Clone)]
284pub struct LineIndex {
285 line_starts: Vec<u32>,
288 source_len: u32,
290}
291
292impl LineIndex {
293 pub fn new(source: &str) -> Self {
298 let mut line_starts = vec![0u32];
299 for (i, byte) in source.bytes().enumerate() {
300 if byte == b'\n' {
301 line_starts.push((i + 1) as u32);
302 }
303 }
304 Self {
305 line_starts,
306 source_len: source.len() as u32,
307 }
308 }
309
310 #[inline]
319 pub fn line_number(&self, offset: u32) -> usize {
320 debug_assert!(
321 offset <= self.source_len,
322 "offset {} exceeds source length {}",
323 offset,
324 self.source_len
325 );
326 let offset = offset.min(self.source_len);
327
328 self.line_starts.partition_point(|&start| start <= offset)
334 }
335
336 #[inline]
338 pub fn span_line_number(&self, span: Span) -> usize {
339 self.line_number(span.start)
340 }
341
342 #[inline]
354 pub fn line_col(&self, offset: u32) -> (usize, usize) {
355 debug_assert!(
356 offset <= self.source_len,
357 "offset {} exceeds source length {}",
358 offset,
359 self.source_len
360 );
361 let offset = offset.min(self.source_len);
362
363 let line_idx = self.line_starts.partition_point(|&start| start <= offset);
365 let line_start = self.line_starts[line_idx - 1];
367 let col = (offset - line_start) as usize + 1;
368 (line_idx, col)
369 }
370
371 #[inline]
375 pub fn span_line_col(&self, span: Span) -> (usize, usize) {
376 self.line_col(span.start)
377 }
378
379 #[inline]
381 pub fn line_count(&self) -> usize {
382 self.line_starts.len()
383 }
384}
385
386impl From<std::ops::Range<usize>> for Span {
387 #[inline]
388 fn from(range: std::ops::Range<usize>) -> Self {
389 Self {
390 file_id: FileId::DEFAULT,
391 start: range.start as u32,
392 end: range.end as u32,
393 }
394 }
395}
396
397impl From<std::ops::Range<u32>> for Span {
398 #[inline]
399 fn from(range: std::ops::Range<u32>) -> Self {
400 Self {
401 file_id: FileId::DEFAULT,
402 start: range.start,
403 end: range.end,
404 }
405 }
406}
407
408#[cfg(test)]
409mod tests {
410 use super::*;
411
412 #[test]
413 fn test_span_size() {
414 assert_eq!(std::mem::size_of::<Span>(), 12);
416 }
417
418 #[test]
419 fn test_file_id() {
420 assert_eq!(FileId::DEFAULT.index(), 0);
421 assert_eq!(FileId::new(42).index(), 42);
422 }
423
424 #[test]
425 fn test_span_with_file() {
426 let file = FileId::new(5);
427 let span = Span::with_file(file, 10, 20);
428 assert_eq!(span.file_id, file);
429 assert_eq!(span.start, 10);
430 assert_eq!(span.end, 20);
431 }
432
433 #[test]
434 fn test_span_point_in_file() {
435 let file = FileId::new(3);
436 let span = Span::point_in_file(file, 15);
437 assert_eq!(span.file_id, file);
438 assert_eq!(span.start, 15);
439 assert_eq!(span.end, 15);
440 }
441
442 #[test]
443 fn test_span_cover_preserves_file_id() {
444 let file = FileId::new(7);
445 let a = Span::with_file(file, 5, 10);
446 let b = Span::with_file(file, 15, 20);
447 let covered = Span::cover(a, b);
448 assert_eq!(covered.file_id, file);
449 assert_eq!(covered.start, 5);
450 assert_eq!(covered.end, 20);
451 }
452
453 #[test]
454 fn test_span_cover() {
455 let a = Span::new(5, 10);
456 let b = Span::new(15, 20);
457 let covered = Span::cover(a, b);
458 assert_eq!(covered, Span::new(5, 20));
459 }
460
461 #[test]
462 fn test_span_from_range() {
463 let span: Span = (5usize..10usize).into();
464 assert_eq!(span.start, 5);
465 assert_eq!(span.end, 10);
466 }
467
468 #[test]
469 fn test_byte_offset_to_line() {
470 let source = "line1\nline2\nline3";
471 assert_eq!(byte_offset_to_line(source, 0), 1);
473 assert_eq!(byte_offset_to_line(source, 4), 1);
474 assert_eq!(byte_offset_to_line(source, 6), 2);
476 assert_eq!(byte_offset_to_line(source, 10), 2);
477 assert_eq!(byte_offset_to_line(source, 12), 3);
479 assert_eq!(byte_offset_to_line(source, 16), 3);
480 }
481
482 #[test]
483 fn test_span_line_number() {
484 let source = "let x = 1;\nlet y = 2;\nlet z = 3;";
485 let span1 = Span::new(0, 10);
487 assert_eq!(span1.line_number(source), 1);
488 let span2 = Span::new(11, 21);
490 assert_eq!(span2.line_number(source), 2);
491 let span3 = Span::new(22, 32);
493 assert_eq!(span3.line_number(source), 3);
494 }
495
496 #[test]
497 fn test_byte_offset_to_line_at_bounds() {
498 let source = "hello";
499 assert_eq!(byte_offset_to_line(source, 5), 1);
501 assert_eq!(byte_offset_to_line(source, 100), 1);
503 }
504
505 #[test]
506 fn test_byte_offset_to_line_empty_source() {
507 let source = "";
508 assert_eq!(byte_offset_to_line(source, 0), 1);
510 }
511
512 #[test]
513 fn test_span_line_number_at_newline() {
514 let source = "a\nb";
515 let span = Span::new(1, 2);
517 assert_eq!(span.line_number(source), 1);
518 let span2 = Span::new(2, 3);
520 assert_eq!(span2.line_number(source), 2);
521 }
522
523 #[test]
528 fn test_line_index_basic() {
529 let source = "line1\nline2\nline3";
530 let index = LineIndex::new(source);
531
532 assert_eq!(index.line_number(0), 1);
534 assert_eq!(index.line_number(4), 1);
535
536 assert_eq!(index.line_number(6), 2);
538 assert_eq!(index.line_number(10), 2);
539
540 assert_eq!(index.line_number(12), 3);
542 assert_eq!(index.line_number(16), 3);
543 }
544
545 #[test]
546 fn test_line_index_matches_byte_offset_to_line() {
547 let source = "let x = 1;\nlet y = 2;\nlet z = 3;";
548 let index = LineIndex::new(source);
549
550 for offset in 0..=source.len() {
552 assert_eq!(
553 index.line_number(offset as u32),
554 byte_offset_to_line(source, offset),
555 "mismatch at offset {}",
556 offset
557 );
558 }
559 }
560
561 #[test]
562 fn test_line_index_empty_source() {
563 let source = "";
564 let index = LineIndex::new(source);
565 assert_eq!(index.line_number(0), 1);
566 assert_eq!(index.line_count(), 1);
567 }
568
569 #[test]
570 fn test_line_index_single_line() {
571 let source = "hello";
572 let index = LineIndex::new(source);
573 assert_eq!(index.line_number(0), 1);
574 assert_eq!(index.line_number(4), 1);
575 assert_eq!(index.line_count(), 1);
576 }
577
578 #[test]
579 fn test_line_index_at_newline() {
580 let source = "a\nb";
581 let index = LineIndex::new(source);
582 assert_eq!(index.line_number(1), 1);
584 assert_eq!(index.line_number(2), 2);
586 }
587
588 #[test]
589 fn test_line_index_trailing_newline() {
590 let source = "line1\n";
591 let index = LineIndex::new(source);
592 assert_eq!(index.line_number(0), 1);
593 assert_eq!(index.line_number(5), 1); assert_eq!(index.line_number(6), 2); assert_eq!(index.line_count(), 2);
596 }
597
598 #[test]
599 fn test_line_index_span_line_number() {
600 let source = "let x = 1;\nlet y = 2;\nlet z = 3;";
601 let index = LineIndex::new(source);
602
603 let span1 = Span::new(0, 10);
604 assert_eq!(index.span_line_number(span1), 1);
605
606 let span2 = Span::new(11, 21);
607 assert_eq!(index.span_line_number(span2), 2);
608
609 let span3 = Span::new(22, 32);
610 assert_eq!(index.span_line_number(span3), 3);
611 }
612
613 #[test]
614 fn test_line_index_at_bounds() {
615 let source = "hello";
616 let index = LineIndex::new(source);
617 assert_eq!(index.line_number(5), 1);
619 }
620
621 #[test]
622 fn test_line_index_line_count() {
623 assert_eq!(LineIndex::new("").line_count(), 1);
624 assert_eq!(LineIndex::new("a").line_count(), 1);
625 assert_eq!(LineIndex::new("a\n").line_count(), 2);
626 assert_eq!(LineIndex::new("a\nb").line_count(), 2);
627 assert_eq!(LineIndex::new("a\nb\n").line_count(), 3);
628 assert_eq!(LineIndex::new("a\nb\nc").line_count(), 3);
629 }
630
631 #[test]
636 fn test_byte_offset_to_line_col_basic() {
637 let source = "line1\nline2\nline3";
638 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)); }
653
654 #[test]
655 fn test_byte_offset_to_line_col_empty_source() {
656 let source = "";
657 assert_eq!(byte_offset_to_line_col(source, 0), (1, 1));
658 }
659
660 #[test]
661 fn test_byte_offset_to_line_col_single_line() {
662 let source = "hello";
663 assert_eq!(byte_offset_to_line_col(source, 0), (1, 1));
664 assert_eq!(byte_offset_to_line_col(source, 2), (1, 3));
665 assert_eq!(byte_offset_to_line_col(source, 4), (1, 5));
666 assert_eq!(byte_offset_to_line_col(source, 5), (1, 6)); }
668
669 #[test]
670 fn test_byte_offset_to_line_col_at_newline() {
671 let source = "a\nb";
672 assert_eq!(byte_offset_to_line_col(source, 0), (1, 1));
676 assert_eq!(byte_offset_to_line_col(source, 1), (1, 2));
677 assert_eq!(byte_offset_to_line_col(source, 2), (2, 1));
678 }
679
680 #[test]
681 fn test_span_line_col() {
682 let source = "let x = 1;\nlet y = 2;\nlet z = 3;";
683 let span1 = Span::new(0, 10);
688 assert_eq!(span1.line_col(source), (1, 1));
689
690 let span2 = Span::new(11, 21);
691 assert_eq!(span2.line_col(source), (2, 1));
692
693 let span3 = Span::new(22, 32);
694 assert_eq!(span3.line_col(source), (3, 1));
695
696 let span_mid = Span::new(4, 10); assert_eq!(span_mid.line_col(source), (1, 5)); }
700
701 #[test]
702 fn test_line_index_line_col_basic() {
703 let source = "line1\nline2\nline3";
704 let index = LineIndex::new(source);
705
706 assert_eq!(index.line_col(0), (1, 1));
708 assert_eq!(index.line_col(4), (1, 5));
709
710 assert_eq!(index.line_col(6), (2, 1));
712 assert_eq!(index.line_col(10), (2, 5));
713
714 assert_eq!(index.line_col(12), (3, 1));
716 assert_eq!(index.line_col(16), (3, 5));
717 }
718
719 #[test]
720 fn test_line_index_line_col_matches_byte_offset() {
721 let source = "let x = 1;\nlet y = 2;\nlet z = 3;";
722 let index = LineIndex::new(source);
723
724 for offset in 0..=source.len() {
726 assert_eq!(
727 index.line_col(offset as u32),
728 byte_offset_to_line_col(source, offset),
729 "mismatch at offset {}",
730 offset
731 );
732 }
733 }
734
735 #[test]
736 fn test_line_index_span_line_col() {
737 let source = "let x = 1;\nlet y = 2;\nlet z = 3;";
738 let index = LineIndex::new(source);
739
740 let span1 = Span::new(0, 10);
741 assert_eq!(index.span_line_col(span1), (1, 1));
742
743 let span2 = Span::new(11, 21);
744 assert_eq!(index.span_line_col(span2), (2, 1));
745
746 let span3 = Span::new(22, 32);
747 assert_eq!(index.span_line_col(span3), (3, 1));
748
749 let span_mid = Span::new(4, 10);
751 assert_eq!(index.span_line_col(span_mid), (1, 5));
752 }
753
754 #[test]
755 fn test_line_index_line_col_empty_source() {
756 let source = "";
757 let index = LineIndex::new(source);
758 assert_eq!(index.line_col(0), (1, 1));
759 }
760
761 #[test]
762 fn test_line_index_line_col_at_newline() {
763 let source = "a\nb";
764 let index = LineIndex::new(source);
765 assert_eq!(index.line_col(0), (1, 1)); assert_eq!(index.line_col(1), (1, 2)); assert_eq!(index.line_col(2), (2, 1)); }
769
770 #[test]
775 fn test_span_contains_span() {
776 let outer = Span::new(5, 20);
777
778 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))); }
791
792 #[test]
793 fn test_span_contains_point() {
794 let outer = Span::new(5, 20);
795
796 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))); }
805
806 #[test]
807 fn test_span_contains_empty_span() {
808 let empty = Span::point(10);
809
810 assert!(empty.contains(Span::point(10)));
812 assert!(!empty.contains(Span::point(9)));
813 assert!(!empty.contains(Span::new(10, 11)));
814 }
815
816 #[test]
817 fn test_span_contains_pos() {
818 let span = Span::new(5, 10);
819
820 assert!(!span.contains_pos(0));
822 assert!(!span.contains_pos(4));
823
824 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));
832 assert!(!span.contains_pos(100));
833 }
834
835 #[test]
836 fn test_span_contains_pos_empty_span() {
837 let empty = Span::point(10);
838
839 assert!(!empty.contains_pos(9));
841 assert!(!empty.contains_pos(10));
842 assert!(!empty.contains_pos(11));
843 }
844}