use alloc::vec::Vec;
use diag_lang::{Diagnostic, Label, Severity};
use syntax_lang::{Element, Node, Span, TokenKind};
use crate::{
Language, Parse, codes,
grammar::{InjectionDef, Lex},
kind::Kind,
parse::Injection,
parser,
scan::{MAIN, PartRt, StringKinds},
};
const MAX_DEPTH: u32 = 16;
pub(crate) fn finish<'a>(
language: &'a Language,
source: &'a str,
tree: Node<Kind>,
mut diagnostics: Vec<Diagnostic>,
) -> Parse<'a> {
let grammar = language.tables();
let Some(extra) = grammar.extra.as_deref() else {
return Parse::new(language, source, tree, diagnostics, Vec::new());
};
if extra.injections.is_empty() && extra.embedded_parse.is_empty() {
return Parse::new(language, source, tree, diagnostics, Vec::new());
}
let Lex::V2(scanner) = &grammar.lexer else {
return Parse::new(language, source, tree, diagnostics, Vec::new());
};
let mut injections: Vec<Injection<'a>> = Vec::new();
let mut found: Vec<Target<'a>> = Vec::new();
collect(&tree, extra, &grammar.kinds, scanner, source, 1, &mut found);
let mut next = 0;
while next < found.len() {
let target = found[next].clone();
next += 1;
let parsed = match target.rule {
Some(rule) if target.depth > MAX_DEPTH => {
let _ = rule;
diagnostics.push(
Diagnostic::new(
Severity::Warning,
"this injection is nested more than 16 levels deep and was not parsed",
Label::unlabelled(target.span),
)
.with_code(codes::PARSE_TOO_DEEP),
);
None
}
Some(rule) => {
let (start, end) = (target.span.start().to_usize(), target.span.end().to_usize());
let mut tokens = Vec::new();
let mut diags = Vec::new();
scanner.run_range(source, start, end, MAIN, &mut tokens, &mut diags);
let (tree, diags) = parser::parse_tokens(grammar, source, &tokens, rule, diags);
diagnostics.extend(diags);
collect(
&tree,
extra,
&grammar.kinds,
scanner,
source,
target.depth + 1,
&mut found,
);
Some(tree)
}
None => None,
};
injections.push(Injection::new(
target.id,
target.language,
target.span,
parsed,
target.editor,
));
}
diagnostics.sort_by_key(|d| d.primary().span().start().to_u32());
injections.sort_by_key(|i| (i.span().start().to_u32(), i.span().end().to_u32()));
Parse::new(language, source, tree, diagnostics, injections)
}
#[derive(Clone)]
struct Target<'a> {
id: &'a str,
language: &'a str,
span: Span,
rule: Option<u32>,
editor: bool,
depth: u32,
}
fn collect<'a>(
tree: &Node<Kind>,
extra: &'a crate::grammar::Extra,
kinds: &'a crate::grammar::Kinds,
scanner: &crate::scan::Scanner,
source: &str,
depth: u32,
out: &mut Vec<Target<'a>>,
) {
let mut stack: Vec<&Node<Kind>> = Vec::from([tree]);
while let Some(node) = stack.pop() {
for child in node.children() {
let kind = *child.kind();
if kind.is_trivia() {
continue;
}
let index = kind.index();
if let Ok(at) = extra
.embedded_parse
.binary_search_by_key(&index, |(k, _)| *k)
{
let (_, rule) = extra.embedded_parse[at];
out.push(Target {
id: kinds.name_at(usize::from(index)),
language: "self",
span: child.span(),
rule: Some(rule),
editor: false,
depth,
});
}
for def in extra.injections.iter() {
let hit = match def.label {
None => def.kind == index,
Some(label) => node.kind().index() == def.kind && kind.label() == Some(label),
};
if hit {
out.push(Target {
id: &def.id,
language: &def.language,
span: content(def, child, scanner, source),
rule: (&*def.language == "self" && !def.editor).then_some(def.start),
editor: def.editor,
depth,
});
}
}
if let Element::Node(n) = child {
stack.push(n);
}
}
}
}
fn content(
def: &InjectionDef,
element: &Element<Kind>,
scanner: &crate::scan::Scanner,
source: &str,
) -> Span {
let span = element.span();
if !def.inner {
return span;
}
let Element::Token(token) = element else {
return span;
};
let text = &source[span.start().to_usize()..span.end().to_usize()];
for class in scanner.strings.iter() {
let StringKinds::Token(kind) = class.kinds else {
continue;
};
if kind != *token.kind() {
continue;
}
if let ([PartRt::Text(open)], [PartRt::Text(close)]) = (&*class.open, &*class.close) {
if text.len() >= open.len() + close.len()
&& text.as_bytes().starts_with(open)
&& text.as_bytes().ends_with(close)
{
return Span::new(
span.start().to_u32() + open.len() as u32,
span.end().to_u32() - close.len() as u32,
);
}
}
}
span
}