pub struct SourceMap { /* private fields */ }Expand description
A collection of sources laid out end to end in a single global position space.
A SourceMap is the multi-file coordinate layer of a front-end. Each source
added to it gets a stable SourceId and a non-overlapping range in one
shared position space, so a single global BytePos names a point across the
whole project. locate maps such a position back to its
(SourceId, local offset) — the inverse of the layout — which is how a
diagnostic rendered against a global span knows which file to point at.
§Layout
Sources are placed in the order they are added: the first occupies
0..len₀, the next len₀..len₀ + len₁, and so on. Because the bases only
increase, the internal list is always sorted by start offset, so a lookup is a
binary search over it — O(log files) — with no separate index to maintain.
The whole space is 32 bits wide, the same envelope a single
BytePos addresses, so the combined length of every source is capped at
u32::MAX; overrunning it is the SpaceExhausted error, never a silent
wrap into a neighbour’s range.
§Examples
use source_lang::{BytePos, SourceMap};
let mut map = SourceMap::new();
let main = map.add("main.rs", "fn main() {}").expect("fits"); // global 0..12
let util = map.add("util.rs", "fn helper() {}").expect("fits"); // global 12..26
// A global position resolves to the file it lands in and the local offset.
let (id, local) = map.locate(BytePos::new(13)).expect("inside util.rs");
assert_eq!(id, util);
assert_eq!(local, BytePos::new(1)); // 13 - 12
assert_eq!(map.source(id).unwrap().name(), "util.rs");
// Position 0 is the very start of the first file.
assert_eq!(map.locate(BytePos::new(0)).unwrap().0, main);
// Anything past the last byte belongs to no file.
assert_eq!(map.locate(BytePos::new(26)), None);Implementations§
Source§impl SourceMap
impl SourceMap
Sourcepub const fn new() -> Self
pub const fn new() -> Self
Creates an empty map whose global position space starts at 0.
§Examples
use source_lang::SourceMap;
let map = SourceMap::new();
assert!(map.is_empty());Sourcepub fn with_capacity(capacity: usize) -> Self
pub fn with_capacity(capacity: usize) -> Self
Creates an empty map with room for capacity sources preallocated.
A hint only: it sizes the internal list so that adding up to capacity
sources does not reallocate, which matters when the source count is known
up front. The global position space still starts empty.
§Examples
use source_lang::SourceMap;
let mut map = SourceMap::with_capacity(2);
map.add("a", "x").expect("fits");
map.add("b", "y").expect("fits");
assert_eq!(map.len(), 2);Sourcepub fn add(
&mut self,
name: impl Into<Box<str>>,
text: impl Into<Box<str>>,
) -> Result<SourceId, SourceMapError>
pub fn add( &mut self, name: impl Into<Box<str>>, text: impl Into<Box<str>>, ) -> Result<SourceId, SourceMapError>
Adds a source under name with the given text, returning its
SourceId.
The source is appended after every existing one: it takes the range
next..next + text.len() where next is the current end of the global
space. Both name and text are taken by value (anything that converts
into a Box<str> — a String or a &str), so the map owns the text and
callers can borrow it back for the life of the map.
Adding an empty text is allowed: it yields a valid id whose source has a
zero-width span and does not advance the global space, so it can never be
the target of a locate.
§Errors
Returns SourceMapError::SpaceExhausted if text does not fit in the
bytes left in the 32-bit global space, or if the map already holds the
maximum number of sources. The map is left unchanged, so the failure is
recoverable.
§Examples
use source_lang::SourceMap;
let mut map = SourceMap::new();
let id = map.add("config.toml", "name = \"demo\"").expect("fits");
assert_eq!(map.source(id).unwrap().text(), "name = \"demo\"");
// A String works just as well as a &str.
let owned = String::from("generated");
let _ = map.add("out.txt", owned).expect("fits");Sourcepub fn locate(&self, pos: BytePos) -> Option<(SourceId, BytePos)>
pub fn locate(&self, pos: BytePos) -> Option<(SourceId, BytePos)>
Resolves a global position to the source it falls in and the local offset within that source.
The returned BytePos is pos minus the source’s base, i.e. the offset
into SourceFile::text. Resolution is a binary search over the sources’
start offsets, so it is O(log files) and borrows the located source
rather than copying it.
Returns None when pos belongs to no source: past the end of the last
one, or — since a zero-width source contains no position — at the exact
offset of an empty source. The membership is half-open: a source covering
start..end contains start but not end, so the boundary between two
adjacent sources resolves to the second, never to both.
§Examples
use source_lang::{BytePos, SourceMap};
let mut map = SourceMap::new();
let a = map.add("a", "abc").expect("fits"); // 0..3
let b = map.add("b", "de").expect("fits"); // 3..5
assert_eq!(map.locate(BytePos::new(2)), Some((a, BytePos::new(2))));
// The shared boundary at 3 is the start of `b`, not the end of `a`.
assert_eq!(map.locate(BytePos::new(3)), Some((b, BytePos::new(0))));
assert_eq!(map.locate(BytePos::new(5)), None);Sourcepub fn source(&self, id: SourceId) -> Option<&SourceFile>
pub fn source(&self, id: SourceId) -> Option<&SourceFile>
Borrows the source named by id, or None if the id is not from this map.
§Examples
use source_lang::SourceMap;
let mut map = SourceMap::new();
let id = map.add("readme.md", "# title").expect("fits");
assert_eq!(map.source(id).unwrap().name(), "readme.md");Sourcepub fn len(&self) -> usize
pub fn len(&self) -> usize
Returns the number of sources in the map.
§Examples
use source_lang::SourceMap;
let mut map = SourceMap::new();
assert_eq!(map.len(), 0);
map.add("a", "x").expect("fits");
assert_eq!(map.len(), 1);Sourcepub fn is_empty(&self) -> bool
pub fn is_empty(&self) -> bool
Returns true if the map holds no sources.
§Examples
use source_lang::SourceMap;
let mut map = SourceMap::new();
assert!(map.is_empty());
map.add("a", "x").expect("fits");
assert!(!map.is_empty());Sourcepub fn iter(
&self,
) -> impl ExactSizeIterator<Item = (SourceId, &SourceFile)> + '_
pub fn iter( &self, ) -> impl ExactSizeIterator<Item = (SourceId, &SourceFile)> + '_
Iterates over the sources in insertion order, pairing each with its id.
The order is also id order (0, 1, …) and global-offset order, so the
iterator walks the global position space from start to end. Useful for
listing the loaded files or building a side table keyed by SourceId.
§Examples
use source_lang::SourceMap;
let mut map = SourceMap::new();
map.add("a.txt", "one").expect("fits");
map.add("b.txt", "two").expect("fits");
let names: Vec<_> = map.iter().map(|(_, f)| f.name()).collect();
assert_eq!(names, ["a.txt", "b.txt"]);