Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/rust.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ on:

env:
CARGO_TERM_COLOR: always
NIGHTLY: 'nightly-2023-09-30'
NIGHTLY: 'nightly-2025-01-01'

jobs:
check_lints:
Expand Down
2 changes: 1 addition & 1 deletion byteyarn/src/boxed.rs
Original file line number Diff line number Diff line change
Expand Up @@ -587,7 +587,7 @@ impl<'a> YarnBox<'a, [u8]> {
}
}

impl<'a, T> YarnBox<'a, [T]>
impl<T> YarnBox<'_, [T]>
where
[T]: crate::Buf,
{
Expand Down
2 changes: 1 addition & 1 deletion byteyarn/src/reffed.rs
Original file line number Diff line number Diff line change
Expand Up @@ -293,7 +293,7 @@ impl<'a> YarnRef<'a, [u8]> {
}
}

impl<'a> YarnRef<'a, str> {
impl YarnRef<'_, str> {
/// Converts this yarn into a string slice.
pub fn as_str(&self) -> &str {
self.as_slice()
Expand Down
89 changes: 23 additions & 66 deletions ilex/src/file/context.rs
Original file line number Diff line number Diff line change
@@ -1,40 +1,36 @@
use std::collections::HashMap;
use std::fs;
use std::sync::Arc;
use std::sync::RwLock;
use std::sync::RwLockReadGuard;

use camino::Utf8Path;
use camino::Utf8PathBuf;

use crate::f;
use crate::file::File;
use crate::file::SpanId;
use crate::file::CTX_FOR_SPAN_DEBUG;
use crate::report;
use crate::report::Fatal;
use crate::report::Report;

use super::Span;
#[cfg(doc)]
use crate::Span;

/// A source context, which owns source code files.
///
/// A `Context` contains the full text of all the loaded source files, which
/// [`SpanId`]s ultimately refer to. Most [`SpanId`] operations need their
/// corresponding `Context` available.
/// [`Span`]s ultimately refer to.
#[derive(Default)]
pub struct Context {
state: Arc<RwLock<State>>,
}

#[derive(Default)]
pub struct State {
// Each file is laid out as the length of the text, followed by the text data,
// followed by the path.
//
// TODO(mcyoung): Be smarter about this and use something something concurrent
// vector? We don't need to have all this stuff behind a lock I think.
files: Vec<(Utf8PathBuf, String)>,

ranges: Vec<Span>,
comments: HashMap<(u32, u32), Vec<SpanId>>,
files: Vec<(usize, String)>,
}

unsafe impl Send for Context {}
Expand Down Expand Up @@ -84,19 +80,21 @@ impl Context {
}

/// Adds a new file to this source context.
pub fn new_file(
pub fn new_file<'a>(
&self,
name: impl Into<Utf8PathBuf>,
path: impl Into<&'a Utf8Path>,
text: impl Into<String>,
) -> File {
let mut text = text.into();
text.push(' '); // This space only exists to be somewhere for an EOF span
// to point to in diagnostics; user code will never see
// it.
let len = text.len();
text.push_str(path.into().as_str());

let idx = {
let mut state = self.state.write().unwrap();
state.files.push((name.into(), text));
state.files.push((len, text));
state.files.len() - 1
};

Expand All @@ -105,85 +103,44 @@ impl Context {

/// Adds a new file to this source context by opening `name` and reading it
/// from the file system.
pub fn open_file(
pub fn open_file<'a>(
&self,
name: impl Into<Utf8PathBuf>,
path: impl Into<&'a Utf8Path>,
report: &Report,
) -> Result<File, Fatal> {
let name = name.into();
let path = path.into();

let bytes = match fs::read(&name) {
let bytes = match fs::read(path) {
Ok(bytes) => bytes,
Err(e) => {
report.error(f!("could not open input file `{name}`: {e}"));
report.error(f!("could not open input file `{path}`: {e}"));
return report.fatal();
}
};

let Ok(utf8) = String::from_utf8(bytes) else {
report.error(f!("input file `{name}` was not valid UTF-8"));
report.error(f!("input file `{path}` was not valid UTF-8"));
return report.fatal();
};

Ok(self.new_file(name, utf8))
Ok(self.new_file(path, utf8))
}

/// Gets the `idx`th file in this source context.
pub fn file(&self, idx: usize) -> Option<File> {
let state = self.state.read().unwrap();
let (path, text) = state.files.get(idx)?;
let (path, text) = unsafe {
let (len, text) = state.files.get(idx)?;
let text = unsafe {
// SAFETY: The pointer to the file's text is immutable and pointer-stable,
// so we can safely extend its lifetime here.
(&*(path.as_path() as *const Utf8Path), &*(text.as_str() as *const str))
&*(text.as_str() as *const str)
};

Some(File { path, text, ctx: self, idx })
Some(File { len: *len, text, ctx: self, idx })
}

/// Gets the number of files currently tracked by this source context.
pub fn file_count(&self) -> usize {
self.state.read().unwrap().files.len()
}

/// Gets the byte range for the given span, if it isn't the synthetic span.
pub(crate) fn lookup_range(&self, span: SpanId) -> Span {
let state = self.state.read().unwrap();
state.ranges[span.0 as usize]
}

pub(crate) fn lookup_comments(
&self,
file: File,
offset: usize,
) -> (RwLockReadGuard<State>, *const [SpanId]) {
let state = self.state.read().unwrap();
let ptr = state
.comments
.get(&(file.idx as u32, offset as u32))
.map(|x| x.as_slice())
.unwrap_or_default() as *const [SpanId];
(state, ptr)
}

pub(crate) fn add_comment(&self, file: File, offset: usize, comment: SpanId) {
self
.state
.write()
.unwrap()
.comments
.entry((file.idx as u32, offset as u32))
.or_default()
.push(comment)
}

/// Creates a new synthetic span with the given contents.
pub(crate) fn new_span(&self, range: Span) -> SpanId {
let mut state = self.state.write().unwrap();
assert!(state.ranges.len() <= (u32::MAX as usize), "ran out of spans");

let span = SpanId(state.ranges.len() as u32);
state.ranges.push(range);
span
}
}
Loading
Loading