use std::collections::{HashMap, VecDeque};
use pine_lexer::{Token, TokenType};
pub(crate) enum Lead {
Comment(String),
Blank,
}
pub(crate) struct Comments {
leading: VecDeque<(u32, Lead)>,
trailing: HashMap<u32, String>,
}
impl Comments {
pub(crate) fn extract(tokens: &[Token]) -> Self {
let mut leading = VecDeque::new();
let mut trailing = HashMap::new();
let mut last_code_line: Option<usize> = None;
for token in tokens {
match &token.typ {
TokenType::Comment(text) => {
let rendered = render(text);
if last_code_line == Some(token.line) {
trailing.insert(token.line as u32, rendered);
} else {
leading.push_back((token.line as u32, Lead::Comment(rendered)));
}
}
TokenType::BlankLine => leading.push_back((token.line as u32, Lead::Blank)),
TokenType::Newline | TokenType::Indent | TokenType::Dedent | TokenType::Eof => {}
_ => last_code_line = Some(token.line),
}
}
Self { leading, trailing }
}
pub(crate) fn take_leading(&mut self, line: Option<u32>) -> Vec<Lead> {
let mut out = Vec::new();
let Some(line) = line else { return out };
while let Some((trivia_line, _)) = self.leading.front() {
if *trivia_line < line {
out.push(self.leading.pop_front().expect("front exists").1);
} else {
break;
}
}
out
}
pub(crate) fn take_trailing(&mut self, line: Option<u32>) -> Option<String> {
line.and_then(|line| self.trailing.remove(&line))
}
pub(crate) fn drain_trailing(&mut self) -> Vec<String> {
let mut rest: Vec<(u32, String)> = self.trailing.drain().collect();
rest.sort_by_key(|(line, _)| *line);
rest.into_iter().map(|(_, text)| text).collect()
}
}
fn render(text: &str) -> String {
format!("//{}", text.trim_end())
}