use crate::lang::common::{PAT_HTML_TAG, PAT_HTML_SPACE, PAT_SPACE};
#[derive(Debug, Clone)]
pub enum TokType { Word, Space, TagOpen, TagClose, TagSelf, Spacer }
#[derive(Debug, Clone)]
pub struct Token {
pub value: String,
pub tag: String, pub kind: TokType,
}
impl Token {
pub fn new_tag(raw: &str) -> Self {
let mut head = raw[1..raw.len()-1].to_lowercase(); let kind = if head.starts_with('/') {
head.remove(0);
TokType::TagClose
} else if raw.ends_with("/>") {
TokType::TagSelf
} else {
TokType::TagOpen
};
let tag_name = head.split_whitespace().next().unwrap_or("").to_string();
let kind = if matches!(kind, TokType::TagOpen|TokType::TagClose|TokType::TagSelf)
&& PAT_HTML_SPACE.is_match(raw).unwrap_or(false)
{
TokType::Spacer
} else { kind };
Token { value: raw.to_string(), tag: tag_name, kind }
}
}
pub fn tokenize_html(line: &str) -> Vec<Token> {
let mut tokens = Vec::new();
let mut start = 0usize;
for m in PAT_HTML_TAG.find_iter(line) {
let m = m.unwrap();
let (s, e) = (m.start(), m.end());
if s > start {
let text = &line[start..s];
split_text(text, &mut tokens);
}
tokens.push(Token::new_tag(&line[s..e]));
start = e;
}
if start < line.len() {
split_text(&line[start..], &mut tokens);
}
tokens
}
fn split_text(text: &str, out: &mut Vec<Token>) {
let mut last = 0usize;
for m in PAT_SPACE.find_iter(text) {
let m = m.unwrap();
let (s, e) = (m.start(), m.end());
if s > last {
out.push(Token { value: text[last..s].to_string(), tag: "".into(), kind: TokType::Word });
}
out.push(Token { value: text[s..e].to_string(), tag: "".into(), kind: TokType::Space });
last = e;
}
if last < text.len() {
out.push(Token { value: text[last..].to_string(), tag: "".into(), kind: TokType::Word });
}
}