ruff_diagnostics/source_map.rs
1use ruff_text_size::{Ranged, TextSize};
2
3use crate::Edit;
4
5/// Lightweight sourcemap marker representing the source and destination
6/// position for an [`Edit`].
7#[derive(Debug, PartialEq, Eq)]
8pub struct SourceMarker {
9 /// Position of the marker in the original source.
10 source: TextSize,
11 /// Position of the marker in the transformed code.
12 dest: TextSize,
13}
14
15impl SourceMarker {
16 pub fn new(source: TextSize, dest: TextSize) -> Self {
17 Self { source, dest }
18 }
19
20 pub const fn source(&self) -> TextSize {
21 self.source
22 }
23
24 pub const fn dest(&self) -> TextSize {
25 self.dest
26 }
27}
28
29/// A collection of [`SourceMarker`].
30///
31/// Sourcemaps are used to map positions in the original source to positions in
32/// the transformed code. Here, only the boundaries of edits are tracked instead
33/// of every single character.
34///
35/// This mapping maintains the invariant that markers are in source order.
36#[derive(Default, PartialEq, Eq)]
37pub struct SourceMap(Vec<SourceMarker>);
38
39impl SourceMap {
40 /// Returns a slice of all the markers in the sourcemap in source order.
41 pub fn markers(&self) -> &[SourceMarker] {
42 &self.0
43 }
44
45 /// Push the start marker for an [`Edit`].
46 ///
47 /// The `output_length` is the length of the transformed string before the
48 /// edit is applied.
49 ///
50 /// ## Panics
51 ///
52 /// If the start of `edit` is less than previous markers.
53 pub fn push_start_marker(&mut self, edit: &Edit, output_length: TextSize) {
54 self.push_marker(edit.start(), output_length);
55 }
56
57 /// Push the end marker for an [`Edit`].
58 ///
59 /// The `output_length` is the length of the transformed string after the
60 /// edit has been applied.
61 ///
62 /// ## Panics
63 ///
64 /// If `edit` falls before previous markers.
65 pub fn push_end_marker(&mut self, edit: &Edit, output_length: TextSize) {
66 if edit.is_insertion() {
67 self.push_marker(edit.start(), output_length);
68 } else {
69 // Deletion or replacement
70 self.push_marker(edit.end(), output_length);
71 }
72 }
73
74 /// Push a new marker to the sourcemap.
75 ///
76 /// ## Panics
77 ///
78 /// If `offset` is less than previous markers.
79 pub fn push_marker(&mut self, offset: TextSize, output_length: TextSize) {
80 assert!(
81 self.0.last().is_none_or(|last| offset >= last.source),
82 "Markers must be pushed in source order",
83 );
84
85 self.0.push(SourceMarker {
86 source: offset,
87 dest: output_length,
88 });
89 }
90}