use crate::cursor::script_detector::ScriptLanguage;
use crate::syntax::{
plugin::{SyntaxPlugin, SyntaxPluginContext, SyntaxResult},
types::{CssStyleContent, OxcScriptContent, StyleLang, SyntaxEvent, SyntaxTagType},
};
#[derive(Debug, Clone)]
pub enum BlockType {
Script,
ScriptSetup,
Template,
Style,
}
#[derive(Debug, Clone)]
pub struct ExtractedBlockInternal {
pub block_type: BlockType,
pub content: String,
pub lang: Option<String>,
pub scoped: bool,
pub module: bool,
pub start: u32,
pub end: u32,
}
pub struct BlockExtractorPlugin {
pub scripts: Vec<ExtractedBlockInternal>,
pub styles: Vec<ExtractedBlockInternal>,
pub template_start: Option<u32>,
pub template_end: Option<u32>,
}
impl BlockExtractorPlugin {
pub fn new() -> Self {
Self {
scripts: Vec::new(),
styles: Vec::new(),
template_start: None,
template_end: None,
}
}
fn extract_script(&mut self, script: &OxcScriptContent, source: &str) {
let content_start = script.content_start as usize;
let content_end = script.content_end as usize;
let content = if content_start < content_end && content_end <= source.len() {
source[content_start..content_end].to_string()
} else {
String::new()
};
let block_type = if script.setup.is_some() {
BlockType::ScriptSetup
} else {
BlockType::Script
};
let lang = script.lang.and_then(|l| match l {
ScriptLanguage::JavaScript => Some("js".to_string()),
ScriptLanguage::TypeScript => Some("ts".to_string()),
ScriptLanguage::JSX => Some("jsx".to_string()),
ScriptLanguage::TSX => Some("tsx".to_string()),
ScriptLanguage::Unknown => None,
});
self.scripts.push(ExtractedBlockInternal {
block_type,
content,
lang,
scoped: false,
module: false,
start: script.content_start,
end: script.content_end,
});
}
fn extract_style(&mut self, style: &CssStyleContent, source: &str) {
let content_start = style.content_start as usize;
let content_end = style.content_end as usize;
let content = if content_start < content_end && content_end <= source.len() {
source[content_start..content_end].to_string()
} else {
String::new()
};
let lang = style.lang.map(|l| match l {
StyleLang::Css => "css".to_string(),
StyleLang::Scss => "scss".to_string(),
StyleLang::Sass => "sass".to_string(),
StyleLang::Less => "less".to_string(),
StyleLang::Stylus => "stylus".to_string(),
});
self.styles.push(ExtractedBlockInternal {
block_type: BlockType::Style,
content,
lang,
scoped: style.scoped,
module: style.module.is_some(),
start: style.content_start,
end: style.content_end,
});
}
}
impl Default for BlockExtractorPlugin {
fn default() -> Self {
Self::new()
}
}
impl<'a> SyntaxPlugin<'a> for BlockExtractorPlugin {
fn name(&self) -> &str {
"block_extractor"
}
fn process_event(
&mut self,
event: SyntaxEvent<'a>,
ctx: &mut SyntaxPluginContext<'a>,
) -> SyntaxResult<SyntaxEvent<'a>> {
match &event {
SyntaxEvent::OpenTagEnd(tag) if tag.tag_type == SyntaxTagType::Template => {
self.template_start = Some(tag.end);
}
SyntaxEvent::OxcScriptContent(script) => {
self.extract_script(script, ctx.input);
}
SyntaxEvent::CssStyleContent(style) => {
self.extract_style(style, ctx.input);
}
_ => {}
}
SyntaxResult::Keep(event)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_block_extractor_plugin_new() {
let plugin = BlockExtractorPlugin::new();
assert!(plugin.scripts.is_empty());
assert!(plugin.styles.is_empty());
assert!(plugin.template_start.is_none());
}
}