use super::{Context, Indexed, Matcher, Row, Rows, Stored, lines};
use crate::unit::{Mark, Scope};
fn context(scope: Scope<'_>) -> Option<&[u8]> {
scope.macro_name.or(scope.function)
}
fn walk<'u>(indexed: &Indexed<'u>, mut visit: impl FnMut(&Stored<'u, '_>, usize, Scope<'u>)) {
let mut scope = indexed.start;
let mut lines = lines(indexed.unit);
while let Some(stored) = lines.next() {
for (index, name) in stored.names.iter().enumerate() {
scope.read(name);
visit(&stored, index, scope);
}
scope.leave(&stored.line);
}
}
pub(super) fn symbols<'u>(
matcher: &mut Matcher<'_>,
files: &[Indexed<'u>],
assignments: bool,
rows: &mut Rows<'u>,
) {
if assignments {
symbols_for::<true>(matcher, files, rows);
} else {
symbols_for::<false>(matcher, files, rows);
}
}
fn symbols_for<'u, const ASSIGNMENTS: bool>(
matcher: &mut Matcher<'_>,
files: &[Indexed<'u>],
rows: &mut Rows<'u>,
) {
for indexed in files {
let mut answered = false;
walk(indexed, |stored, index, scope| {
if index == 0 {
answered = false;
}
let name = stored.names[index];
if answered || (ASSIGNMENTS && !name.assigned) {
return;
}
let matched = match name.mark {
Mark::FunctionEnd | Mark::DefineEnd | Mark::Include => false,
Mark::Plain => matcher.plain(name.text),
_ => matcher.marked(name.text),
};
if !matched {
return;
}
let context = if name.mark == Mark::Define {
scope.function
} else {
context(scope)
};
rows.push(Row {
path: indexed.path,
context: context.map_or(Context::Global, Context::Name),
line: stored.line.number,
text: stored.text(index),
});
answered = true;
});
}
}
pub(super) fn callers<'u>(matcher: &mut Matcher<'_>, files: &[Indexed<'u>], rows: &mut Rows<'u>) {
for indexed in files {
walk(indexed, |stored, index, scope| {
let name = stored.names[index];
if name.mark != Mark::Call || !matcher.marked(name.text) {
return;
}
if let Some(caller) = context(scope) {
rows.push(Row {
path: indexed.path,
context: Context::Name(caller),
line: stored.line.number,
text: stored.text(index),
});
}
});
}
}
pub(super) fn callees<'u>(matcher: &mut Matcher<'_>, files: &[Indexed<'u>], rows: &mut Rows<'u>) {
for indexed in files {
let mut asked: Option<(&'u [u8], bool)> = None;
walk(indexed, |stored, index, scope| {
let name = stored.names[index];
if name.mark != Mark::Call {
return;
}
let Some(caller) = context(scope) else {
return;
};
let matched = match asked {
Some((last, matched)) if std::ptr::eq(last, caller) => matched,
_ => {
let matched = matcher.marked(caller);
asked = Some((caller, matched));
matched
}
};
if matched {
rows.push(Row {
path: indexed.path,
context: Context::Name(name.text),
line: stored.line.number,
text: stored.text(index),
});
}
});
}
}
#[cfg(test)]
mod tests;