2022-12-18 10:02:32 +00:00
use nom ::branch ::alt ;
use nom ::bytes ::complete ::tag ;
2022-12-18 11:31:40 +00:00
use nom ::character ::complete ::anychar ;
2022-12-18 10:02:32 +00:00
use nom ::character ::complete ::digit1 ;
2022-12-18 11:31:40 +00:00
use nom ::character ::complete ::line_ending ;
2022-12-18 10:02:32 +00:00
use nom ::character ::complete ::one_of ;
2023-03-17 20:37:47 +00:00
use nom ::character ::complete ::space0 ;
2022-12-18 12:18:42 +00:00
use nom ::combinator ::consumed ;
2022-12-18 11:31:40 +00:00
use nom ::combinator ::not ;
2022-12-18 12:18:42 +00:00
use nom ::combinator ::opt ;
2023-03-18 18:36:33 +00:00
use nom ::combinator ::peek ;
2022-12-18 10:02:32 +00:00
use nom ::combinator ::recognize ;
2023-03-18 21:32:07 +00:00
use nom ::combinator ::verify ;
2023-03-17 20:37:47 +00:00
use nom ::multi ::many0_count ;
2022-12-18 11:31:40 +00:00
use nom ::multi ::many1 ;
2022-12-18 10:02:32 +00:00
use nom ::sequence ::tuple ;
2022-12-18 12:18:42 +00:00
use super ::combinator ::context_many_till ;
2023-03-18 21:32:07 +00:00
use super ::error ::CustomError ;
use super ::error ::MyError ;
2022-12-18 08:29:01 +00:00
use super ::error ::Res ;
2023-03-17 20:37:47 +00:00
use super ::paragraph ::paragraph_end ;
2023-03-17 20:49:09 +00:00
use super ::parser_context ::ContextElement ;
2023-03-18 18:36:33 +00:00
use super ::parser_with_context ::parser_with_context ;
2022-12-18 12:18:42 +00:00
use super ::text ::space ;
use super ::text ::text_element ;
2022-12-18 10:02:32 +00:00
use super ::token ::ListItem ;
2022-12-18 08:29:01 +00:00
use super ::token ::PlainList ;
2022-12-18 12:40:52 +00:00
use super ::token ::Token ;
2022-12-18 08:29:01 +00:00
use super ::Context ;
pub fn plain_list < ' r , ' s > ( context : Context < ' r , ' s > , i : & ' s str ) -> Res < & ' s str , PlainList < ' s > > {
// todo
todo! ( )
}
2022-12-18 10:02:32 +00:00
2023-03-17 20:37:47 +00:00
pub fn item < ' r , ' s > ( context : Context < ' r , ' s > , i : & ' s str ) -> Res < & ' s str , ListItem < ' s > > {
let ( remaining , leading_whitespace ) = space0 ( i ) ? ;
let indent_level = leading_whitespace . len ( ) ;
2023-03-17 20:49:09 +00:00
let list_item_context = context . with_additional_node ( ContextElement ::ListItem ( indent_level ) ) ;
2023-03-17 20:37:47 +00:00
let ( remaining , ( bul , countset , check , tg , sp , ( contents , end ) ) ) = tuple ( (
2022-12-18 12:18:42 +00:00
bullet ,
opt ( tuple ( ( space , counter_set ) ) ) ,
opt ( tuple ( ( space , check_box ) ) ) ,
opt ( tuple ( ( space , item_tag ) ) ) ,
space ,
2023-03-17 20:49:09 +00:00
context_many_till ( & list_item_context , text_element , item_end ) ,
2023-03-17 20:37:47 +00:00
) ) ( remaining ) ? ;
2022-12-18 12:40:52 +00:00
let elements = contents
. into_iter ( )
. filter_map ( | token | match token {
Token ::TextElement ( text_element ) = > Some ( text_element ) ,
Token ::Paragraph ( _ ) = > panic! ( " There should only be text elements in items. " ) ,
} )
. collect ( ) ;
2023-03-17 20:37:47 +00:00
let source = {
let offset = remaining . as_ptr ( ) as usize - i . as_ptr ( ) as usize ;
& i [ .. offset ]
} ;
2022-12-18 12:40:52 +00:00
let ret = ListItem {
source ,
2023-03-17 20:37:47 +00:00
leading_whitespace ,
2022-12-18 12:40:52 +00:00
bullet : bul ,
2023-03-17 20:37:47 +00:00
counter_set : countset . map ( | ( _spc , count ) | count ) ,
2022-12-18 12:40:52 +00:00
check_box : check . map ( | ( _spc , check ) | check ) ,
item_tag : tg . map ( | ( _spc , tg ) | tg ) ,
contents : elements ,
} ;
Ok ( ( remaining , ret ) )
2022-12-18 10:02:32 +00:00
}
fn counter < ' s > ( i : & ' s str ) -> Res < & ' s str , & ' s str > {
alt ( ( recognize ( one_of ( " abcdefghijklmnopqrstuvwxyz " ) ) , digit1 ) ) ( i )
}
fn bullet < ' s > ( i : & ' s str ) -> Res < & ' s str , & ' s str > {
alt ( (
tag ( " * " ) ,
tag ( " - " ) ,
tag ( " + " ) ,
recognize ( tuple ( ( counter , alt ( ( tag ( " . " ) , tag ( " ) " ) ) ) ) ) ) ,
) ) ( i )
}
2022-12-18 11:31:40 +00:00
fn counter_set < ' s > ( i : & ' s str ) -> Res < & ' s str , & ' s str > {
recognize ( tuple ( ( tag ( " [@ " ) , counter , tag ( " ] " ) ) ) ) ( i )
}
fn check_box < ' s > ( i : & ' s str ) -> Res < & ' s str , & ' s str > {
recognize ( alt ( ( tag ( " [ ] " ) , tag ( " [X] " ) , tag ( " [-] " ) ) ) ) ( i )
}
fn item_tag < ' s > ( i : & ' s str ) -> Res < & ' s str , & ' s str > {
recognize ( tuple ( ( tag_text , tag_separator ) ) ) ( i )
}
fn tag_text < ' s > ( i : & ' s str ) -> Res < & ' s str , & ' s str > {
recognize ( many1 ( tag_text_character ) ) ( i )
}
fn tag_text_character < ' s > ( i : & ' s str ) -> Res < & ' s str , & ' s str > {
not ( alt ( ( tag_separator , line_ending ) ) ) ( i ) ? ;
recognize ( anychar ) ( i )
}
fn tag_separator < ' s > ( i : & ' s str ) -> Res < & ' s str , & ' s str > {
tag ( " :: " ) ( i )
}
2022-12-18 12:18:42 +00:00
pub fn item_end < ' r , ' s > ( context : Context < ' r , ' s > , i : & ' s str ) -> Res < & ' s str , & ' s str > {
2023-03-18 18:36:33 +00:00
let item_matcher = parser_with_context! ( item ) ( & context ) ;
2023-03-19 17:05:37 +00:00
let line_indented_matcher = parser_with_context! ( line_indented_lte ) ( & context ) ;
2023-03-18 18:36:33 +00:00
alt ( (
paragraph_end ,
2023-03-19 17:05:37 +00:00
recognize ( tuple ( ( line_ending , peek ( line_indented_matcher ) ) ) ) ,
// TODO: Do we still need the item_matcher entry here? If we remove it, then child items should become part of the body of the parent item which would match the description on https://orgmode.org/worg/org-syntax.html
2023-03-18 18:36:33 +00:00
recognize ( tuple ( ( line_ending , peek ( item_matcher ) ) ) ) ,
) ) ( i )
2022-12-18 12:18:42 +00:00
}
2023-03-18 21:32:07 +00:00
fn line_indented_lte < ' r , ' s > ( context : Context < ' r , ' s > , i : & ' s str ) -> Res < & ' s str , & ' s str > {
let current_item_indent_level : & usize = get_context_item_indent ( context ) . ok_or (
nom ::Err ::Error ( CustomError ::MyError ( MyError ( " NotInPlainListItem " ) ) ) ,
) ? ;
let matched = recognize ( verify (
2023-03-19 17:05:37 +00:00
tuple ( ( space0 ::< & str , _ > , anychar ) ) ,
| ( _space0 , _anychar ) | _space0 . len ( ) < = * current_item_indent_level ,
2023-03-18 21:32:07 +00:00
) ) ( i ) ? ;
Ok ( matched )
}
fn get_context_item_indent < ' r , ' s > ( context : Context < ' r , ' s > ) -> Option < & ' r usize > {
for thing in context . iter ( ) {
match thing . get_data ( ) {
ContextElement ::ListItem ( depth ) = > return Some ( depth ) ,
_ = > { }
} ;
}
None
}