use crate::interactive::highlight::{ColorSpan, HighlightStyle};
#[derive(Debug, Clone, PartialEq, Eq)]
pub(super) enum ScanMode {
Normal,
SingleQuote { start: usize },
DoubleQuote { start: usize },
DollarSingleQuote { start: usize },
Parameter { start: usize, braced: bool },
CommandSub { start: usize },
Backtick { start: usize },
ArithSub { start: usize },
Comment { start: usize },
}
#[derive(Debug, Clone)]
pub(super) struct ScannerState {
pub mode_stack: Vec<ScanMode>,
pub word_start: bool,
pub command_position: bool,
}
impl ScannerState {
pub(super) fn new() -> Self {
Self {
mode_stack: vec![ScanMode::Normal],
word_start: true,
command_position: true,
}
}
pub(super) fn current_mode(&self) -> &ScanMode {
self.mode_stack.last().unwrap_or(&ScanMode::Normal)
}
pub(super) fn push_mode(&mut self, mode: ScanMode) {
self.mode_stack.push(mode);
}
pub(super) fn pop_mode(&mut self) {
if self.mode_stack.len() > 1 {
self.mode_stack.pop();
}
}
}
pub(super) fn mark_unclosed_errors(
state: &ScannerState,
input_len: usize,
spans: &mut Vec<ColorSpan>,
) {
for mode in &state.mode_stack {
match mode {
ScanMode::SingleQuote { start }
| ScanMode::DoubleQuote { start }
| ScanMode::DollarSingleQuote { start }
| ScanMode::Backtick { start } => {
spans.push(ColorSpan {
start: *start,
end: input_len,
style: HighlightStyle::Error,
});
}
ScanMode::CommandSub { start } | ScanMode::Parameter { start, .. } => {
spans.push(ColorSpan {
start: *start,
end: (*start + 2).min(input_len),
style: HighlightStyle::Error,
});
}
ScanMode::ArithSub { start } => {
spans.push(ColorSpan {
start: *start,
end: (*start + 3).min(input_len),
style: HighlightStyle::Error,
});
}
ScanMode::Normal | ScanMode::Comment { .. } => {}
}
}
}