pub mod ast;
pub mod lexer;
pub mod parser;
pub mod syntax_kind;
pub use lexer::lex;
pub use parser::{Parse, ParseError, parse, parse_with_cache};
pub use syntax_kind::{InkLanguage, SyntaxElement, SyntaxKind, SyntaxNode, SyntaxToken};
impl Parse {
#[must_use]
#[expect(
clippy::expect_used,
reason = "parse() always produces SOURCE_FILE root"
)]
pub fn tree(&self) -> ast::SourceFile {
use ast::AstNode as _;
ast::SourceFile::cast(self.syntax()).expect("parse always produces a SOURCE_FILE root")
}
}
#[must_use]
pub fn extract_includes(source: &str) -> Vec<String> {
parse(source)
.tree()
.includes()
.filter_map(|inc| inc.file_path())
.map(|fp| fp.text().trim().to_string())
.filter(|s| !s.is_empty())
.collect()
}
#[cfg(test)]
mod extract_includes_tests {
use super::*;
#[test]
fn extracts_top_level_includes() {
let src = "INCLUDE helper.ink\nINCLUDE other.ink\n";
assert_eq!(extract_includes(src), vec!["helper.ink", "other.ink"]);
}
#[test]
fn empty_source_returns_empty() {
assert!(extract_includes("").is_empty());
}
#[test]
fn no_includes_returns_empty() {
assert!(extract_includes("=== knot ===\nhello\n").is_empty());
}
#[test]
fn ignores_commented_out_include() {
let src = "// INCLUDE commented.ink\nINCLUDE real.ink\n";
assert_eq!(extract_includes(src), vec!["real.ink"]);
}
}