#![allow(clippy::redundant_pub_crate)]
pub(crate) use crate::ast::WordSpan;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub(crate) enum QuotingContext {
None,
DoubleQuote,
ParamExpansion,
CommandSub,
Backtick,
}
pub(crate) struct WordBuilder {
pub(crate) value: String,
pub(crate) spans: Vec<WordSpan>,
context_stack: Vec<QuotingContext>,
}
impl WordBuilder {
pub(crate) const fn new() -> Self {
Self {
value: String::new(),
spans: Vec::new(),
context_stack: Vec::new(),
}
}
pub(crate) fn push(&mut self, c: char) {
self.value.push(c);
}
pub(crate) fn ends_with(&self, c: char) -> bool {
self.value.ends_with(c)
}
pub(crate) const fn is_empty(&self) -> bool {
self.value.is_empty()
}
pub(crate) const fn span_start(&self) -> usize {
self.value.len()
}
pub(crate) fn record(&mut self, start: usize, kind: WordSpanKind) {
self.spans.push(WordSpan {
start,
end: self.value.len(),
kind,
context: self.current_context(),
});
}
pub(crate) fn current_context(&self) -> QuotingContext {
self.context_stack
.last()
.copied()
.unwrap_or(QuotingContext::None)
}
pub(crate) fn enter_context(&mut self, ctx: QuotingContext) {
self.context_stack.push(ctx);
}
pub(crate) fn leave_context(&mut self) {
self.context_stack.pop();
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
#[non_exhaustive]
pub(crate) enum WordSpanKind {
CommandSub,
AnsiCQuote,
LocaleString,
ProcessSub(char),
SingleQuoted,
DoubleQuoted,
ParamExpansion,
SimpleVar,
ArithmeticSub,
Backtick,
BracketSubscript,
Extglob(char),
DeprecatedArith,
Escape,
BraceExpansion,
}