Update auto-registry
This commit is contained in:
parent
e6e5c8ffe9
commit
4c6f41f9ff
24 changed files with 333 additions and 314 deletions
|
@ -269,7 +269,7 @@ pub fn generate_registry(attr: TokenStream, input: TokenStream) -> TokenStream {
|
|||
for name in names {
|
||||
let struct_name: proc_macro2::TokenStream = name.parse().unwrap();
|
||||
stream.extend(quote::quote_spanned!(proc_macro2::Span::call_site() =>
|
||||
#struct_name::new(),
|
||||
#struct_name::default(),
|
||||
));
|
||||
}
|
||||
} else {
|
||||
|
|
|
@ -32,6 +32,7 @@ struct QuoteData {
|
|||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
#[auto_registry::auto_registry(registry = "blocks")]
|
||||
pub struct Quote {
|
||||
properties: PropertyParser,
|
||||
}
|
||||
|
|
|
@ -34,11 +34,30 @@ pub trait BlockType: core::fmt::Debug {
|
|||
) -> Result<String, String>;
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct BlockHolder {
|
||||
blocks: HashMap<String, Rc<dyn BlockType>>,
|
||||
}
|
||||
|
||||
macro_rules! create_blocks {
|
||||
( $($construct:expr),+ $(,)? ) => {{
|
||||
let mut map = HashMap::new();
|
||||
$(
|
||||
let val = Rc::new($construct) as Rc<dyn BlockType>;
|
||||
map.insert(val.name().to_string(), val);
|
||||
)+
|
||||
map
|
||||
}};
|
||||
}
|
||||
#[auto_registry::generate_registry(registry = "blocks", target = make_blocks, return_type = HashMap<String, Rc<dyn BlockType>>, maker = create_blocks)]
|
||||
|
||||
impl Default for BlockHolder {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
blocks: make_blocks()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl BlockHolder {
|
||||
pub fn get(&self, layout_name: &str) -> Option<Rc<dyn BlockType>> {
|
||||
self.blocks.get(layout_name).cloned()
|
||||
|
|
|
@ -35,8 +35,8 @@ pub struct BlockRule {
|
|||
continue_re: Regex,
|
||||
}
|
||||
|
||||
impl BlockRule {
|
||||
pub fn new() -> Self {
|
||||
impl Default for BlockRule {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
start_re: Regex::new(
|
||||
r"(?:^|\n)>[^\S\r\n]*(?:\[!((?:\\.|[^\\\\])*?)\])(?:\[((?:\\.|[^\\\\])*?)\])?[^\S\r\n]*",
|
||||
|
|
|
@ -33,8 +33,8 @@ pub struct CodeRule {
|
|||
properties: PropertyParser,
|
||||
}
|
||||
|
||||
impl CodeRule {
|
||||
pub fn new() -> Self {
|
||||
impl Default for CodeRule {
|
||||
fn default() -> Self {
|
||||
let mut props = HashMap::new();
|
||||
props.insert(
|
||||
"line_offset".to_string(),
|
||||
|
@ -71,14 +71,14 @@ impl RegexRule for CodeRule {
|
|||
state: &ParserState,
|
||||
document: &dyn Document,
|
||||
token: Token,
|
||||
matches: Captures,
|
||||
captures: Captures,
|
||||
) -> Vec<Report> {
|
||||
let mut reports = vec![];
|
||||
|
||||
// Properties
|
||||
let prop_source = escape_source(
|
||||
token.source(),
|
||||
matches.get(1).map_or(0..0, |m| m.range()),
|
||||
captures.get(1).map_or(0..0, |m| m.range()),
|
||||
"Code Properties".into(),
|
||||
'\\',
|
||||
"]",
|
||||
|
@ -92,7 +92,7 @@ impl RegexRule for CodeRule {
|
|||
None => return reports,
|
||||
};
|
||||
|
||||
let code_lang = match matches.get(2) {
|
||||
let code_lang = match captures.get(2) {
|
||||
None => "Plain Text".to_string(),
|
||||
Some(lang) => {
|
||||
let mut code_lang = lang.as_str().trim_start().trim_end().to_string();
|
||||
|
@ -124,13 +124,13 @@ impl RegexRule for CodeRule {
|
|||
};
|
||||
|
||||
let mut code_content = if index == 0 {
|
||||
util::escape_text('\\', "```", matches.get(4).unwrap().as_str(), false)
|
||||
util::escape_text('\\', "```", captures.get(4).unwrap().as_str(), false)
|
||||
} else {
|
||||
util::escape_text(
|
||||
'\\',
|
||||
"``",
|
||||
matches.get(3).unwrap().as_str(),
|
||||
!matches.get(3).unwrap().as_str().contains('\n'),
|
||||
captures.get(3).unwrap().as_str(),
|
||||
!captures.get(3).unwrap().as_str().contains('\n'),
|
||||
)
|
||||
};
|
||||
if code_content.bytes().last() == Some(b'\n')
|
||||
|
@ -156,7 +156,7 @@ impl RegexRule for CodeRule {
|
|||
if index == 0
|
||||
// Block
|
||||
{
|
||||
let code_name = matches.get(3).and_then(|name| {
|
||||
let code_name = captures.get(3).and_then(|name| {
|
||||
let code_name = name.as_str().trim_end().trim_start().to_string();
|
||||
(!code_name.is_empty()).then_some(code_name)
|
||||
});
|
||||
|
@ -182,12 +182,12 @@ impl RegexRule for CodeRule {
|
|||
|
||||
// Code Ranges
|
||||
if let Some(coderanges) = CodeRange::from_source(token.source(), &state.shared.lsp) {
|
||||
coderanges.add(matches.get(4).unwrap().range(), code_lang.clone());
|
||||
coderanges.add(captures.get(4).unwrap().range(), code_lang.clone());
|
||||
}
|
||||
|
||||
// Conceals
|
||||
if let Some(conceals) = Conceals::from_source(token.source(), &state.shared.lsp) {
|
||||
let range = matches
|
||||
let range = captures
|
||||
.get(0)
|
||||
.map(|m| {
|
||||
if token.source().content().as_bytes()[m.start()] == b'\n' {
|
||||
|
@ -213,7 +213,7 @@ impl RegexRule for CodeRule {
|
|||
},
|
||||
);
|
||||
|
||||
let range = matches
|
||||
let range = captures
|
||||
.get(0)
|
||||
.map(|m| {
|
||||
if token.source().content().as_bytes()[m.start()] == b'\n' {
|
||||
|
@ -253,7 +253,7 @@ impl RegexRule for CodeRule {
|
|||
// Code Ranges
|
||||
if let Some(coderanges) = CodeRange::from_source(token.source(), &state.shared.lsp) {
|
||||
if block == CodeKind::MiniBlock {
|
||||
let range = matches.get(3).unwrap().range();
|
||||
let range = captures.get(3).unwrap().range();
|
||||
coderanges.add(range.start + 1..range.end, code_lang.clone());
|
||||
}
|
||||
}
|
||||
|
@ -261,7 +261,7 @@ impl RegexRule for CodeRule {
|
|||
// Conceals
|
||||
if let Some(conceals) = Conceals::from_source(token.source(), &state.shared.lsp) {
|
||||
if block == CodeKind::MiniBlock {
|
||||
let range = matches
|
||||
let range = captures
|
||||
.get(0)
|
||||
.map(|m| {
|
||||
if token.source().content().as_bytes()[m.start()] == b'\n' {
|
||||
|
@ -287,7 +287,7 @@ impl RegexRule for CodeRule {
|
|||
},
|
||||
);
|
||||
|
||||
let range = matches
|
||||
let range = captures
|
||||
.get(0)
|
||||
.map(|m| {
|
||||
if token.source().content().as_bytes()[m.start()] == b'\n' {
|
||||
|
@ -307,7 +307,7 @@ impl RegexRule for CodeRule {
|
|||
|
||||
// Semantic
|
||||
if let Some((sems, tokens)) = Semantics::from_source(token.source(), &state.shared.lsp) {
|
||||
let range = matches
|
||||
let range = captures
|
||||
.get(0)
|
||||
.map(|m| {
|
||||
if token.source().content().as_bytes()[m.start()] == b'\n' {
|
||||
|
@ -321,15 +321,15 @@ impl RegexRule for CodeRule {
|
|||
range.start..range.start + if index == 0 { 3 } else { 2 },
|
||||
tokens.code_sep,
|
||||
);
|
||||
if let Some(props) = matches.get(1).map(|m| m.range()) {
|
||||
if let Some(props) = captures.get(1).map(|m| m.range()) {
|
||||
sems.add(props.start - 1..props.start, tokens.code_props_sep);
|
||||
sems.add(props.end..props.end + 1, tokens.code_props_sep);
|
||||
}
|
||||
if let Some(lang) = matches.get(2).map(|m| m.range()) {
|
||||
if let Some(lang) = captures.get(2).map(|m| m.range()) {
|
||||
sems.add(lang.clone(), tokens.code_lang);
|
||||
}
|
||||
if index == 0 {
|
||||
if let Some(title) = matches.get(3).map(|m| m.range()) {
|
||||
if let Some(title) = captures.get(3).map(|m| m.range()) {
|
||||
sems.add(title.clone(), tokens.code_title);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -38,8 +38,8 @@ pub struct CommentRule {
|
|||
re: [Regex; 1],
|
||||
}
|
||||
|
||||
impl CommentRule {
|
||||
pub fn new() -> Self {
|
||||
impl Default for CommentRule {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
re: [Regex::new(r"(?:(?:^|\n)|[^\S\n]+)::(.*)").unwrap()],
|
||||
}
|
||||
|
|
|
@ -152,12 +152,9 @@ impl RuleState for CustomStyleState {
|
|||
static STATE_NAME: &str = "elements.custom_style";
|
||||
|
||||
#[auto_registry::auto_registry(registry = "rules", path = "crate::elements::customstyle")]
|
||||
#[derive(Default)]
|
||||
pub struct CustomStyleRule;
|
||||
|
||||
impl CustomStyleRule {
|
||||
pub fn new() -> Self { Self {} }
|
||||
}
|
||||
|
||||
impl Rule for CustomStyleRule {
|
||||
fn name(&self) -> &'static str { "Custom Style" }
|
||||
|
||||
|
|
|
@ -25,35 +25,35 @@ pub struct ElemStyleRule {
|
|||
start_re: Regex,
|
||||
}
|
||||
|
||||
impl ElemStyleRule {
|
||||
pub fn new() -> Self {
|
||||
impl Default for ElemStyleRule {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
start_re: Regex::new(r"(?:^|\n)@@(.*?)=\s*\{").unwrap(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Finds the json substring inside aother string
|
||||
pub fn json_substring(str: &str) -> Option<&str> {
|
||||
let mut in_string = false;
|
||||
let mut brace_depth = 0;
|
||||
let mut escaped = false;
|
||||
/// Finds the json substring inside aother string
|
||||
fn json_substring(str: &str) -> Option<&str> {
|
||||
let mut in_string = false;
|
||||
let mut brace_depth = 0;
|
||||
let mut escaped = false;
|
||||
|
||||
for (pos, c) in str.char_indices() {
|
||||
match c {
|
||||
'{' if !in_string => brace_depth += 1,
|
||||
'}' if !in_string => brace_depth -= 1,
|
||||
'\\' if in_string => escaped = !escaped,
|
||||
'"' if !escaped => in_string = !in_string,
|
||||
_ => escaped = false,
|
||||
}
|
||||
|
||||
if brace_depth == 0 {
|
||||
return Some(&str[..=pos]);
|
||||
}
|
||||
for (pos, c) in str.char_indices() {
|
||||
match c {
|
||||
'{' if !in_string => brace_depth += 1,
|
||||
'}' if !in_string => brace_depth -= 1,
|
||||
'\\' if in_string => escaped = !escaped,
|
||||
'"' if !escaped => in_string = !in_string,
|
||||
_ => escaped = false,
|
||||
}
|
||||
|
||||
None
|
||||
if brace_depth == 0 {
|
||||
return Some(&str[..=pos]);
|
||||
}
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
|
||||
impl Rule for ElemStyleRule {
|
||||
|
@ -127,7 +127,7 @@ impl Rule for ElemStyleRule {
|
|||
};
|
||||
|
||||
// Get value
|
||||
let new_style = match ElemStyleRule::json_substring(
|
||||
let new_style = match json_substring(
|
||||
&cursor.source.clone().content().as_str()[cursor.pos..],
|
||||
) {
|
||||
None => {
|
||||
|
|
|
@ -160,8 +160,8 @@ pub struct GraphRule {
|
|||
properties: PropertyParser,
|
||||
}
|
||||
|
||||
impl GraphRule {
|
||||
pub fn new() -> Self {
|
||||
impl Default for GraphRule {
|
||||
fn default() -> Self {
|
||||
let mut props = HashMap::new();
|
||||
props.insert(
|
||||
"layout".to_string(),
|
||||
|
|
|
@ -22,21 +22,21 @@ pub struct ImportRule {
|
|||
re: [Regex; 1],
|
||||
}
|
||||
|
||||
impl ImportRule {
|
||||
pub fn new() -> Self {
|
||||
impl Default for ImportRule {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
re: [Regex::new(r"(?:^|\n)@import(?:\[(.*)\])?[^\S\r\n]+(.*)").unwrap()],
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn validate_name(_colors: &ReportColors, name: &str) -> Result<String, String> {
|
||||
Ok(name.to_string())
|
||||
}
|
||||
fn validate_name(_colors: &ReportColors, name: &str) -> Result<String, String> {
|
||||
Ok(name.to_string())
|
||||
}
|
||||
|
||||
pub fn validate_as(_colors: &ReportColors, as_name: &str) -> Result<String, String> {
|
||||
// TODO: Use variable name validation rules
|
||||
Ok(as_name.to_string())
|
||||
}
|
||||
fn validate_as(_colors: &ReportColors, as_name: &str) -> Result<String, String> {
|
||||
// TODO: Use variable name validation rules
|
||||
Ok(as_name.to_string())
|
||||
}
|
||||
|
||||
impl RegexRule for ImportRule {
|
||||
|
@ -60,7 +60,7 @@ impl RegexRule for ImportRule {
|
|||
|
||||
// Path
|
||||
let import_file = match matches.get(2) {
|
||||
Some(name) => match ImportRule::validate_name(state.parser.colors(), name.as_str()) {
|
||||
Some(name) => match validate_name(state.parser.colors(), name.as_str()) {
|
||||
Err(msg) => {
|
||||
report_err!(
|
||||
&mut reports,
|
||||
|
@ -121,7 +121,7 @@ impl RegexRule for ImportRule {
|
|||
|
||||
// [Optional] import as
|
||||
let import_as = match matches.get(1) {
|
||||
Some(as_name) => match ImportRule::validate_as(state.parser.colors(), as_name.as_str())
|
||||
Some(as_name) => match validate_as(state.parser.colors(), as_name.as_str())
|
||||
{
|
||||
Ok(as_name) => as_name,
|
||||
Err(msg) => {
|
||||
|
|
|
@ -340,8 +340,8 @@ pub struct LayoutRule {
|
|||
re: [Regex; 3],
|
||||
}
|
||||
|
||||
impl LayoutRule {
|
||||
pub fn new() -> Self {
|
||||
impl Default for LayoutRule {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
re: [
|
||||
RegexBuilder::new(
|
||||
|
@ -365,44 +365,44 @@ impl LayoutRule {
|
|||
],
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn initialize_state(state: &ParserState) -> Rc<RefCell<dyn RuleState>> {
|
||||
let mut rule_state_borrow = state.shared.rule_state.borrow_mut();
|
||||
match rule_state_borrow.get(STATE_NAME) {
|
||||
Some(state) => state,
|
||||
None => {
|
||||
// Insert as a new state
|
||||
match rule_state_borrow.insert(
|
||||
STATE_NAME.into(),
|
||||
Rc::new(RefCell::new(LayoutState { stack: vec![] })),
|
||||
) {
|
||||
Err(err) => panic!("{err}"),
|
||||
Ok(state) => state,
|
||||
}
|
||||
pub fn initialize_state(state: &ParserState) -> Rc<RefCell<dyn RuleState>> {
|
||||
let mut rule_state_borrow = state.shared.rule_state.borrow_mut();
|
||||
match rule_state_borrow.get(STATE_NAME) {
|
||||
Some(state) => state,
|
||||
None => {
|
||||
// Insert as a new state
|
||||
match rule_state_borrow.insert(
|
||||
STATE_NAME.into(),
|
||||
Rc::new(RefCell::new(LayoutState { stack: vec![] })),
|
||||
) {
|
||||
Err(err) => panic!("{err}"),
|
||||
Ok(state) => state,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn parse_properties<'a>(
|
||||
mut reports: &mut Vec<Report>,
|
||||
state: &ParserState,
|
||||
token: &Token,
|
||||
layout_type: Rc<dyn LayoutType>,
|
||||
m: Option<Match>,
|
||||
) -> Option<Box<dyn Any>> {
|
||||
let prop_source = escape_source(
|
||||
token.source(),
|
||||
m.map_or(0..0, |m| m.range()),
|
||||
format!("Layout {} Properties", layout_type.name()),
|
||||
'\\',
|
||||
"]",
|
||||
);
|
||||
layout_type.parse_properties(
|
||||
reports,
|
||||
state,
|
||||
Token::new(0..prop_source.content().len(), prop_source),
|
||||
)
|
||||
}
|
||||
pub fn parse_properties<'a>(
|
||||
mut reports: &mut Vec<Report>,
|
||||
state: &ParserState,
|
||||
token: &Token,
|
||||
layout_type: Rc<dyn LayoutType>,
|
||||
m: Option<Match>,
|
||||
) -> Option<Box<dyn Any>> {
|
||||
let prop_source = escape_source(
|
||||
token.source(),
|
||||
m.map_or(0..0, |m| m.range()),
|
||||
format!("Layout {} Properties", layout_type.name()),
|
||||
'\\',
|
||||
"]",
|
||||
);
|
||||
layout_type.parse_properties(
|
||||
reports,
|
||||
state,
|
||||
Token::new(0..prop_source.content().len(), prop_source),
|
||||
)
|
||||
}
|
||||
|
||||
static STATE_NAME: &str = "elements.layout";
|
||||
|
@ -426,7 +426,7 @@ impl RegexRule for LayoutRule {
|
|||
) -> Vec<Report> {
|
||||
let mut reports = vec![];
|
||||
|
||||
let rule_state = LayoutRule::initialize_state(state);
|
||||
let rule_state = initialize_state(state);
|
||||
|
||||
if index == 0
|
||||
// BEGIN_LAYOUT
|
||||
|
@ -504,7 +504,7 @@ impl RegexRule for LayoutRule {
|
|||
};
|
||||
|
||||
// Parse properties
|
||||
let properties = match LayoutRule::parse_properties(
|
||||
let properties = match parse_properties(
|
||||
&mut reports,
|
||||
state,
|
||||
&token,
|
||||
|
@ -612,7 +612,7 @@ impl RegexRule for LayoutRule {
|
|||
}
|
||||
|
||||
// Parse properties
|
||||
let properties = match LayoutRule::parse_properties(
|
||||
let properties = match parse_properties(
|
||||
&mut reports,
|
||||
state,
|
||||
&token,
|
||||
|
@ -698,7 +698,7 @@ impl RegexRule for LayoutRule {
|
|||
}
|
||||
|
||||
// Parse properties
|
||||
let properties = match LayoutRule::parse_properties(
|
||||
let properties = match parse_properties(
|
||||
&mut reports,
|
||||
state,
|
||||
&token,
|
||||
|
@ -774,7 +774,7 @@ impl RegexRule for LayoutRule {
|
|||
CTX.with_borrow_mut(|ctx| {
|
||||
ctx.as_mut().map(|ctx| {
|
||||
// Make sure the rule state has been initialized
|
||||
let rule_state = LayoutRule::initialize_state(ctx.state);
|
||||
let rule_state = initialize_state(ctx.state);
|
||||
|
||||
// Get layout
|
||||
//
|
||||
|
|
|
@ -82,8 +82,8 @@ pub struct LinkRule {
|
|||
re: [Regex; 1],
|
||||
}
|
||||
|
||||
impl LinkRule {
|
||||
pub fn new() -> Self {
|
||||
impl Default for LinkRule {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
re: [Regex::new(r"\[((?:\\.|[^\\\\])*?)\]\(((?:\\.|[^\\\\])*?)\)").unwrap()],
|
||||
}
|
||||
|
|
|
@ -165,8 +165,8 @@ pub struct ListRule {
|
|||
properties: PropertyParser,
|
||||
}
|
||||
|
||||
impl ListRule {
|
||||
pub fn new() -> Self {
|
||||
impl Default for ListRule {
|
||||
fn default() -> Self {
|
||||
let mut props = HashMap::new();
|
||||
props.insert(
|
||||
"offset".to_string(),
|
||||
|
@ -184,97 +184,97 @@ impl ListRule {
|
|||
properties: PropertyParser { properties: props },
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn push_markers(
|
||||
token: &Token,
|
||||
state: &ParserState,
|
||||
document: &dyn Document,
|
||||
current: &Vec<(bool, usize)>,
|
||||
target: &Vec<(bool, usize)>,
|
||||
) {
|
||||
let mut start_pos = 0;
|
||||
for i in 0..std::cmp::min(target.len(), current.len()) {
|
||||
if current[i].0 != target[i].0 {
|
||||
break;
|
||||
}
|
||||
|
||||
start_pos += 1;
|
||||
fn push_markers(
|
||||
token: &Token,
|
||||
state: &ParserState,
|
||||
document: &dyn Document,
|
||||
current: &Vec<(bool, usize)>,
|
||||
target: &Vec<(bool, usize)>,
|
||||
) {
|
||||
let mut start_pos = 0;
|
||||
for i in 0..std::cmp::min(target.len(), current.len()) {
|
||||
if current[i].0 != target[i].0 {
|
||||
break;
|
||||
}
|
||||
|
||||
// Close
|
||||
for i in start_pos..current.len() {
|
||||
state.push(
|
||||
document,
|
||||
Box::new(ListMarker {
|
||||
location: token.clone(),
|
||||
kind: MarkerKind::Close,
|
||||
numbered: current[current.len() - 1 - (i - start_pos)].0,
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
// Open
|
||||
for i in start_pos..target.len() {
|
||||
state.push(
|
||||
document,
|
||||
Box::new(ListMarker {
|
||||
location: token.clone(),
|
||||
kind: MarkerKind::Open,
|
||||
numbered: target[i].0,
|
||||
}),
|
||||
);
|
||||
}
|
||||
start_pos += 1;
|
||||
}
|
||||
|
||||
fn parse_depth(depth: &str, document: &dyn Document, offset: usize) -> Vec<(bool, usize)> {
|
||||
let mut parsed = vec![];
|
||||
let prev_entry = document
|
||||
.last_element::<ListEntry>()
|
||||
.and_then(|entry| Ref::filter_map(entry, |e| Some(&e.numbering)).ok());
|
||||
// Close
|
||||
for i in start_pos..current.len() {
|
||||
state.push(
|
||||
document,
|
||||
Box::new(ListMarker {
|
||||
location: token.clone(),
|
||||
kind: MarkerKind::Close,
|
||||
numbered: current[current.len() - 1 - (i - start_pos)].0,
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
let mut continue_match = true;
|
||||
depth.chars().enumerate().for_each(|(idx, c)| {
|
||||
let number = if offset == usize::MAX || idx + 1 != depth.len() {
|
||||
prev_entry
|
||||
.as_ref()
|
||||
.and_then(|v| {
|
||||
if !continue_match {
|
||||
return None;
|
||||
}
|
||||
let numbered = c == '-';
|
||||
// Open
|
||||
for i in start_pos..target.len() {
|
||||
state.push(
|
||||
document,
|
||||
Box::new(ListMarker {
|
||||
location: token.clone(),
|
||||
kind: MarkerKind::Open,
|
||||
numbered: target[i].0,
|
||||
}),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
match v.get(idx) {
|
||||
None => None,
|
||||
Some((prev_numbered, prev_idx)) => {
|
||||
if *prev_numbered != numbered {
|
||||
continue_match = false;
|
||||
None
|
||||
}
|
||||
// New depth
|
||||
else if idx + 1 == v.len() {
|
||||
Some(*prev_idx + 1)
|
||||
}
|
||||
// Increase from previous
|
||||
else {
|
||||
Some(*prev_idx)
|
||||
} // Do nothing
|
||||
fn parse_depth(depth: &str, document: &dyn Document, offset: usize) -> Vec<(bool, usize)> {
|
||||
let mut parsed = vec![];
|
||||
let prev_entry = document
|
||||
.last_element::<ListEntry>()
|
||||
.and_then(|entry| Ref::filter_map(entry, |e| Some(&e.numbering)).ok());
|
||||
|
||||
let mut continue_match = true;
|
||||
depth.chars().enumerate().for_each(|(idx, c)| {
|
||||
let number = if offset == usize::MAX || idx + 1 != depth.len() {
|
||||
prev_entry
|
||||
.as_ref()
|
||||
.and_then(|v| {
|
||||
if !continue_match {
|
||||
return None;
|
||||
}
|
||||
let numbered = c == '-';
|
||||
|
||||
match v.get(idx) {
|
||||
None => None,
|
||||
Some((prev_numbered, prev_idx)) => {
|
||||
if *prev_numbered != numbered {
|
||||
continue_match = false;
|
||||
None
|
||||
}
|
||||
// New depth
|
||||
else if idx + 1 == v.len() {
|
||||
Some(*prev_idx + 1)
|
||||
}
|
||||
// Increase from previous
|
||||
else {
|
||||
Some(*prev_idx)
|
||||
} // Do nothing
|
||||
}
|
||||
})
|
||||
.unwrap_or(1)
|
||||
} else {
|
||||
offset
|
||||
};
|
||||
}
|
||||
})
|
||||
.unwrap_or(1)
|
||||
} else {
|
||||
offset
|
||||
};
|
||||
|
||||
match c {
|
||||
'*' => parsed.push((false, number)),
|
||||
'-' => parsed.push((true, number)),
|
||||
_ => panic!("Unimplemented"),
|
||||
}
|
||||
});
|
||||
match c {
|
||||
'*' => parsed.push((false, number)),
|
||||
'-' => parsed.push((true, number)),
|
||||
_ => panic!("Unimplemented"),
|
||||
}
|
||||
});
|
||||
|
||||
parsed
|
||||
}
|
||||
parsed
|
||||
}
|
||||
|
||||
impl Rule for ListRule {
|
||||
|
@ -358,7 +358,7 @@ impl Rule for ListRule {
|
|||
};
|
||||
|
||||
// Depth
|
||||
let depth = ListRule::parse_depth(
|
||||
let depth = parse_depth(
|
||||
captures.get(1).unwrap().as_str(),
|
||||
document,
|
||||
offset.unwrap_or(usize::MAX),
|
||||
|
@ -506,9 +506,9 @@ impl Rule for ListRule {
|
|||
.last_element::<ListEntry>()
|
||||
.map(|ent| ent.numbering.clone())
|
||||
{
|
||||
ListRule::push_markers(&token, state, document, &previous_depth, &depth);
|
||||
push_markers(&token, state, document, &previous_depth, &depth);
|
||||
} else {
|
||||
ListRule::push_markers(&token, state, document, &vec![], &depth);
|
||||
push_markers(&token, state, document, &vec![], &depth);
|
||||
}
|
||||
|
||||
state.push(
|
||||
|
@ -535,7 +535,7 @@ impl Rule for ListRule {
|
|||
.map(|ent| ent.numbering.clone())
|
||||
.unwrap();
|
||||
let token = Token::new(end_cursor.pos..end_cursor.pos, end_cursor.source.clone());
|
||||
ListRule::push_markers(&token, state, document, ¤t, &Vec::new());
|
||||
push_markers(&token, state, document, ¤t, &Vec::new());
|
||||
|
||||
(end_cursor, reports)
|
||||
}
|
||||
|
|
|
@ -237,8 +237,8 @@ pub struct MediaRule {
|
|||
properties: PropertyParser,
|
||||
}
|
||||
|
||||
impl MediaRule {
|
||||
pub fn new() -> Self {
|
||||
impl Default for MediaRule {
|
||||
fn default() -> Self {
|
||||
let mut props = HashMap::new();
|
||||
props.insert(
|
||||
"type".to_string(),
|
||||
|
@ -263,30 +263,31 @@ impl MediaRule {
|
|||
}
|
||||
}
|
||||
|
||||
fn validate_uri(uri: &str) -> Result<&str, String> {
|
||||
let trimmed = uri.trim_start().trim_end();
|
||||
}
|
||||
|
||||
if trimmed.is_empty() {
|
||||
return Err("URIs is empty".to_string());
|
||||
}
|
||||
fn validate_uri(uri: &str) -> Result<&str, String> {
|
||||
let trimmed = uri.trim_start().trim_end();
|
||||
|
||||
Ok(trimmed)
|
||||
if trimmed.is_empty() {
|
||||
return Err("URIs is empty".to_string());
|
||||
}
|
||||
|
||||
fn detect_filetype(filename: &str) -> Option<MediaType> {
|
||||
let sep = match filename.rfind('.') {
|
||||
Some(pos) => pos,
|
||||
None => return None,
|
||||
};
|
||||
Ok(trimmed)
|
||||
}
|
||||
|
||||
// TODO: https://developer.mozilla.org/en-US/docs/Web/Media/Formats/Containers
|
||||
match filename.split_at(sep + 1).1.to_ascii_lowercase().as_str() {
|
||||
"png" | "apng" | "avif" | "gif" | "webp" | "svg" | "bmp" | "jpg" | "jpeg" | "jfif"
|
||||
fn detect_filetype(filename: &str) -> Option<MediaType> {
|
||||
let sep = match filename.rfind('.') {
|
||||
Some(pos) => pos,
|
||||
None => return None,
|
||||
};
|
||||
|
||||
// TODO: https://developer.mozilla.org/en-US/docs/Web/Media/Formats/Containers
|
||||
match filename.split_at(sep + 1).1.to_ascii_lowercase().as_str() {
|
||||
"png" | "apng" | "avif" | "gif" | "webp" | "svg" | "bmp" | "jpg" | "jpeg" | "jfif"
|
||||
| "pjpeg" | "pjp" => Some(MediaType::IMAGE),
|
||||
"mp4" | "m4v" | "webm" | "mov" => Some(MediaType::VIDEO),
|
||||
"mp3" | "ogg" | "flac" | "wav" => Some(MediaType::AUDIO),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -327,7 +328,7 @@ impl RegexRule for MediaRule {
|
|||
|
||||
let uri = match (
|
||||
matches.get(2).unwrap(),
|
||||
MediaRule::validate_uri(matches.get(2).unwrap().as_str()),
|
||||
validate_uri(matches.get(2).unwrap().as_str()),
|
||||
) {
|
||||
(_, Ok(uri)) => util::escape_text('\\', ")", uri, true),
|
||||
(m, Err(err)) => {
|
||||
|
@ -372,7 +373,7 @@ impl RegexRule for MediaRule {
|
|||
) {
|
||||
(Some(media_type), Some(caption), Some(width)) => {
|
||||
if media_type.is_none() {
|
||||
match Self::detect_filetype(uri.as_str()) {
|
||||
match detect_filetype(uri.as_str()) {
|
||||
None => {
|
||||
report_err!(
|
||||
&mut reports,
|
||||
|
@ -509,7 +510,7 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
fn regex() {
|
||||
let rule = MediaRule::new();
|
||||
let rule = MediaRule::default();
|
||||
let re = &rule.regexes()[0];
|
||||
|
||||
assert!(re.is_match("[some properties] some description"));
|
||||
|
|
|
@ -92,8 +92,8 @@ pub struct ParagraphRule {
|
|||
re: Regex,
|
||||
}
|
||||
|
||||
impl ParagraphRule {
|
||||
pub fn new() -> Self {
|
||||
impl Default for ParagraphRule {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
re: Regex::new(r"\n{2,}").unwrap(),
|
||||
}
|
||||
|
|
|
@ -53,8 +53,8 @@ pub struct RawRule {
|
|||
properties: PropertyParser,
|
||||
}
|
||||
|
||||
impl RawRule {
|
||||
pub fn new() -> Self {
|
||||
impl Default for RawRule {
|
||||
fn default() -> Self {
|
||||
let mut props = HashMap::new();
|
||||
props.insert(
|
||||
"kind".to_string(),
|
||||
|
|
|
@ -161,8 +161,8 @@ pub struct ReferenceRule {
|
|||
properties: PropertyParser,
|
||||
}
|
||||
|
||||
impl ReferenceRule {
|
||||
pub fn new() -> Self {
|
||||
impl Default for ReferenceRule {
|
||||
fn default() -> Self {
|
||||
let mut props = HashMap::new();
|
||||
props.insert(
|
||||
"caption".to_string(),
|
||||
|
|
|
@ -29,7 +29,28 @@ pub struct ScriptRule {
|
|||
}
|
||||
|
||||
impl ScriptRule {
|
||||
pub fn new() -> Self {
|
||||
fn validate_kind(&self, colors: &ReportColors, kind: &str) -> Result<usize, String> {
|
||||
match self
|
||||
.eval_kinds
|
||||
.iter()
|
||||
.position(|(kind_symbol, _)| kind == *kind_symbol)
|
||||
{
|
||||
Some(id) => Ok(id),
|
||||
None => Err(format!(
|
||||
"Unable to find eval kind `{}`. Available kinds:{}",
|
||||
kind.fg(colors.highlight),
|
||||
self.eval_kinds
|
||||
.iter()
|
||||
.fold(String::new(), |out, (symbol, name)| {
|
||||
out + format!("\n - '{symbol}' => {name}").as_str()
|
||||
})
|
||||
)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for ScriptRule {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
re: [
|
||||
Regex::new(r"(?:^|\n)@<(?:(.*)\n?)((?:\\.|[^\\\\])*?)(?:\n?)>@").unwrap(),
|
||||
|
@ -43,39 +64,20 @@ impl ScriptRule {
|
|||
],
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn validate_kernel_name(colors: &ReportColors, name: &str) -> Result<String, String> {
|
||||
let trimmed = name.trim_end().trim_start();
|
||||
if trimmed.is_empty() {
|
||||
return Ok("main".to_string());
|
||||
} else if trimmed.find(|c: char| c.is_whitespace()).is_some() {
|
||||
return Err(format!(
|
||||
fn validate_kernel_name(colors: &ReportColors, name: &str) -> Result<String, String> {
|
||||
let trimmed = name.trim_end().trim_start();
|
||||
if trimmed.is_empty() {
|
||||
return Ok("main".to_string());
|
||||
} else if trimmed.find(|c: char| c.is_whitespace()).is_some() {
|
||||
return Err(format!(
|
||||
"Kernel name `{}` contains whitespaces",
|
||||
trimmed.fg(colors.highlight)
|
||||
));
|
||||
}
|
||||
|
||||
Ok(trimmed.to_string())
|
||||
));
|
||||
}
|
||||
|
||||
fn validate_kind(&self, colors: &ReportColors, kind: &str) -> Result<usize, String> {
|
||||
match self
|
||||
.eval_kinds
|
||||
.iter()
|
||||
.position(|(kind_symbol, _)| kind == *kind_symbol)
|
||||
{
|
||||
Some(id) => Ok(id),
|
||||
None => Err(format!(
|
||||
"Unable to find eval kind `{}`. Available kinds:{}",
|
||||
kind.fg(colors.highlight),
|
||||
self.eval_kinds
|
||||
.iter()
|
||||
.fold(String::new(), |out, (symbol, name)| {
|
||||
out + format!("\n - '{symbol}' => {name}").as_str()
|
||||
})
|
||||
)),
|
||||
}
|
||||
}
|
||||
Ok(trimmed.to_string())
|
||||
}
|
||||
|
||||
impl RegexRule for ScriptRule {
|
||||
|
@ -100,7 +102,7 @@ impl RegexRule for ScriptRule {
|
|||
let kernel_name = match matches.get(1) {
|
||||
None => "main".to_string(),
|
||||
Some(name) => {
|
||||
match ScriptRule::validate_kernel_name(state.parser.colors(), name.as_str()) {
|
||||
match validate_kernel_name(state.parser.colors(), name.as_str()) {
|
||||
Ok(name) => name,
|
||||
Err(e) => {
|
||||
report_err!(
|
||||
|
|
|
@ -150,8 +150,8 @@ pub struct SectionRule {
|
|||
re: [Regex; 1],
|
||||
}
|
||||
|
||||
impl SectionRule {
|
||||
pub fn new() -> Self {
|
||||
impl Default for SectionRule {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
re: [Regex::new(r"(?:^|\n)(#{1,})(?:\{(.*)\})?((\*|\+){1,})?(.*)").unwrap()],
|
||||
}
|
||||
|
|
|
@ -137,8 +137,8 @@ pub struct StyleRule {
|
|||
re: [Regex; 4],
|
||||
}
|
||||
|
||||
impl StyleRule {
|
||||
pub fn new() -> Self {
|
||||
impl Default for StyleRule {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
re: [
|
||||
// Bold
|
||||
|
|
|
@ -229,8 +229,8 @@ pub struct TexRule {
|
|||
properties: PropertyParser,
|
||||
}
|
||||
|
||||
impl TexRule {
|
||||
pub fn new() -> Self {
|
||||
impl Default for TexRule {
|
||||
fn default() -> Self {
|
||||
let mut props = HashMap::new();
|
||||
props.insert(
|
||||
"env".to_string(),
|
||||
|
|
|
@ -41,12 +41,9 @@ impl Element for Text {
|
|||
}
|
||||
|
||||
#[auto_registry::auto_registry(registry = "rules", path = "crate::elements::text")]
|
||||
#[derive(Default)]
|
||||
pub struct TextRule;
|
||||
|
||||
impl TextRule {
|
||||
pub fn new() -> Self { Self {} }
|
||||
}
|
||||
|
||||
impl Rule for TextRule {
|
||||
fn name(&self) -> &'static str { "Text" }
|
||||
|
||||
|
|
|
@ -126,8 +126,8 @@ pub struct TocRule {
|
|||
re: [Regex; 1],
|
||||
}
|
||||
|
||||
impl TocRule {
|
||||
pub fn new() -> Self {
|
||||
impl Default for TocRule {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
re: [
|
||||
RegexBuilder::new(r"(?:^|\n)(?:[^\S\n]*)#\+TABLE_OF_CONTENT(.*)")
|
||||
|
|
|
@ -45,13 +45,6 @@ pub struct VariableRule {
|
|||
}
|
||||
|
||||
impl VariableRule {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
re: [Regex::new(r"(?:^|\n)@(')?(.*?)=((?:\\\n|.)*)").unwrap()],
|
||||
kinds: vec![("".into(), "Regular".into()), ("'".into(), "Path".into())],
|
||||
}
|
||||
}
|
||||
|
||||
pub fn make_variable(
|
||||
&self,
|
||||
colors: &ReportColors,
|
||||
|
@ -63,64 +56,73 @@ impl VariableRule {
|
|||
) -> Result<Rc<dyn Variable>, String> {
|
||||
match self.kinds[kind].0.as_str() {
|
||||
"" => Ok(Rc::new(BaseVariable::new(
|
||||
location,
|
||||
name,
|
||||
value_token,
|
||||
value,
|
||||
location,
|
||||
name,
|
||||
value_token,
|
||||
value,
|
||||
))),
|
||||
"'" => {
|
||||
match std::fs::canonicalize(value.as_str()) // TODO: not canonicalize
|
||||
{
|
||||
Ok(path) => Ok(Rc::new(PathVariable::new(location, name, value_token, path))),
|
||||
Err(e) => Err(format!("Unable to canonicalize path `{}`: {}",
|
||||
value.fg(colors.highlight),
|
||||
e))
|
||||
}
|
||||
{
|
||||
Ok(path) => Ok(Rc::new(PathVariable::new(location, name, value_token, path))),
|
||||
Err(e) => Err(format!("Unable to canonicalize path `{}`: {}",
|
||||
value.fg(colors.highlight),
|
||||
e))
|
||||
}
|
||||
}
|
||||
_ => panic!("Unhandled variable kind"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Trim and check variable name for validity
|
||||
pub fn validate_name<'a>(
|
||||
colors: &ReportColors,
|
||||
original_name: &'a str,
|
||||
) -> Result<&'a str, String> {
|
||||
let name = original_name.trim_start().trim_end();
|
||||
if name.contains("%") {
|
||||
return Err(format!("Name cannot contain '{}'", "%".fg(colors.info)));
|
||||
impl Default for VariableRule {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
re: [Regex::new(r"(?:^|\n)@(')?(.*?)=((?:\\\n|.)*)").unwrap()],
|
||||
kinds: vec![("".into(), "Regular".into()), ("'".into(), "Path".into())],
|
||||
}
|
||||
Ok(name)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn validate_value(original_value: &str) -> Result<String, String> {
|
||||
let mut escaped = 0usize;
|
||||
let mut result = String::new();
|
||||
for c in original_value.trim_start().trim_end().chars() {
|
||||
if c == '\\' {
|
||||
escaped += 1
|
||||
} else if c == '\n' {
|
||||
match escaped {
|
||||
0 => return Err("Unknown error wile capturing value".to_string()),
|
||||
// Remove '\n'
|
||||
1 => {}
|
||||
// Insert '\n'
|
||||
_ => {
|
||||
result.push(c);
|
||||
(0..escaped - 2).for_each(|_| result.push('\\'));
|
||||
}
|
||||
// Trim and check variable name for validity
|
||||
pub fn validate_name<'a>(
|
||||
colors: &ReportColors,
|
||||
original_name: &'a str,
|
||||
) -> Result<&'a str, String> {
|
||||
let name = original_name.trim_start().trim_end();
|
||||
if name.contains("%") {
|
||||
return Err(format!("Name cannot contain '{}'", "%".fg(colors.info)));
|
||||
}
|
||||
Ok(name)
|
||||
}
|
||||
|
||||
pub fn validate_value(original_value: &str) -> Result<String, String> {
|
||||
let mut escaped = 0usize;
|
||||
let mut result = String::new();
|
||||
for c in original_value.trim_start().trim_end().chars() {
|
||||
if c == '\\' {
|
||||
escaped += 1
|
||||
} else if c == '\n' {
|
||||
match escaped {
|
||||
0 => return Err("Unknown error wile capturing value".to_string()),
|
||||
// Remove '\n'
|
||||
1 => {}
|
||||
// Insert '\n'
|
||||
_ => {
|
||||
result.push(c);
|
||||
(0..escaped - 2).for_each(|_| result.push('\\'));
|
||||
}
|
||||
escaped = 0;
|
||||
} else {
|
||||
(0..escaped).for_each(|_| result.push('\\'));
|
||||
escaped = 0;
|
||||
result.push(c);
|
||||
}
|
||||
escaped = 0;
|
||||
} else {
|
||||
(0..escaped).for_each(|_| result.push('\\'));
|
||||
escaped = 0;
|
||||
result.push(c);
|
||||
}
|
||||
(0..escaped).for_each(|_| result.push('\\'));
|
||||
|
||||
Ok(result)
|
||||
}
|
||||
(0..escaped).for_each(|_| result.push('\\'));
|
||||
|
||||
Ok(result)
|
||||
}
|
||||
|
||||
impl RegexRule for VariableRule {
|
||||
|
@ -188,7 +190,7 @@ impl RegexRule for VariableRule {
|
|||
};
|
||||
|
||||
let var_name = match matches.get(2) {
|
||||
Some(name) => match VariableRule::validate_name(state.parser.colors(), name.as_str()) {
|
||||
Some(name) => match validate_name(state.parser.colors(), name.as_str()) {
|
||||
Ok(var_name) => var_name,
|
||||
Err(msg) => {
|
||||
report_err!(
|
||||
|
@ -211,7 +213,7 @@ impl RegexRule for VariableRule {
|
|||
};
|
||||
|
||||
let (val_token, var_value) = match matches.get(3) {
|
||||
Some(value) => match VariableRule::validate_value(value.as_str()) {
|
||||
Some(value) => match validate_value(value.as_str()) {
|
||||
Ok(var_value) => (Token::new(value.range(), token.source()), var_value),
|
||||
Err(msg) => {
|
||||
report_err!(
|
||||
|
@ -326,8 +328,8 @@ pub struct VariableSubstitutionRule {
|
|||
re: [Regex; 1],
|
||||
}
|
||||
|
||||
impl VariableSubstitutionRule {
|
||||
pub fn new() -> Self {
|
||||
impl Default for VariableSubstitutionRule {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
re: [Regex::new(r"%(.*?)%").unwrap()],
|
||||
}
|
||||
|
@ -400,7 +402,7 @@ impl RegexRule for VariableSubstitutionRule {
|
|||
return reports;
|
||||
}
|
||||
// Invalid name
|
||||
if let Err(msg) = VariableRule::validate_name(state.parser.colors(), name.as_str())
|
||||
if let Err(msg) = validate_name(state.parser.colors(), name.as_str())
|
||||
{
|
||||
report_err!(
|
||||
&mut reports,
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue