2023-04-21 20:57:49 +00:00
use nom ::branch ::alt ;
use nom ::bytes ::complete ::is_not ;
use nom ::bytes ::complete ::tag_no_case ;
use nom ::character ::complete ::line_ending ;
use nom ::character ::complete ::space0 ;
use nom ::character ::complete ::space1 ;
use nom ::combinator ::consumed ;
use nom ::combinator ::eof ;
use nom ::combinator ::opt ;
use nom ::combinator ::verify ;
use nom ::multi ::many_till ;
use nom ::sequence ::tuple ;
2023-08-23 04:30:26 +00:00
use super ::org_source ::OrgSource ;
2023-04-23 01:45:18 +00:00
use crate ::error ::Res ;
2023-04-21 21:40:49 +00:00
use crate ::parser ::lesser_element ::CommentBlock ;
use crate ::parser ::lesser_element ::ExampleBlock ;
use crate ::parser ::lesser_element ::ExportBlock ;
use crate ::parser ::lesser_element ::SrcBlock ;
use crate ::parser ::lesser_element ::VerseBlock ;
2023-04-21 22:01:06 +00:00
use crate ::parser ::object ::Object ;
use crate ::parser ::object ::PlainText ;
2023-04-21 20:57:49 +00:00
use crate ::parser ::object_parser ::standard_set_object ;
use crate ::parser ::util ::blank_line ;
use crate ::parser ::util ::exit_matcher_parser ;
use crate ::parser ::util ::get_consumed ;
use crate ::parser ::util ::start_of_line ;
2023-04-23 02:06:34 +00:00
use crate ::parser ::util ::text_until_exit ;
2023-04-21 20:31:25 +00:00
2023-08-11 00:04:59 +00:00
#[ cfg_attr(feature = " tracing " , tracing::instrument(ret, level = " debug " )) ]
2023-04-21 21:40:49 +00:00
pub fn verse_block < ' r , ' s > (
2023-09-02 23:16:44 +00:00
context : RefContext < ' r , ' s > ,
2023-08-23 04:30:26 +00:00
input : OrgSource < ' s > ,
) -> Res < OrgSource < ' s > , VerseBlock < ' s > > {
2023-04-21 22:01:06 +00:00
let ( remaining , name ) = lesser_block_begin ( " verse " ) ( context , input ) ? ;
2023-04-21 20:57:49 +00:00
let ( remaining , parameters ) = opt ( tuple ( ( space1 , data ) ) ) ( remaining ) ? ;
let ( remaining , _nl ) = line_ending ( remaining ) ? ;
2023-04-21 22:01:06 +00:00
let lesser_block_end_specialized = lesser_block_end ( " verse " ) ;
2023-04-21 20:57:49 +00:00
let parser_context = context
. with_additional_node ( ContextElement ::ConsumeTrailingWhitespace ( true ) )
. with_additional_node ( ContextElement ::Context ( " lesser block " ) )
. with_additional_node ( ContextElement ::ExitMatcherNode ( ExitMatcherNode {
class : ExitClass ::Beta ,
2023-04-21 21:11:27 +00:00
exit_matcher : & lesser_block_end_specialized ,
2023-04-21 20:57:49 +00:00
} ) ) ;
let parameters = match parameters {
Some ( ( _ws , parameters ) ) = > Some ( parameters ) ,
None = > None ,
} ;
let object_matcher = parser_with_context! ( standard_set_object ) ( & parser_context ) ;
let exit_matcher = parser_with_context! ( exit_matcher_parser ) ( & parser_context ) ;
// Check for a completely empty block
let ( remaining , children ) = match consumed ( many_till ( blank_line , exit_matcher ) ) ( remaining ) {
2023-04-21 22:01:06 +00:00
Ok ( ( remaining , ( whitespace , ( _children , _exit_contents ) ) ) ) = > (
remaining ,
2023-08-23 04:30:26 +00:00
vec! [ Object ::PlainText ( PlainText {
source : whitespace . into ( ) ,
} ) ] ,
2023-04-21 22:01:06 +00:00
) ,
2023-04-21 20:57:49 +00:00
Err ( _ ) = > {
let ( remaining , ( children , _exit_contents ) ) =
many_till ( object_matcher , exit_matcher ) ( remaining ) ? ;
( remaining , children )
}
} ;
2023-04-21 21:11:27 +00:00
let ( remaining , _end ) = lesser_block_end_specialized ( & parser_context , remaining ) ? ;
2023-04-21 20:57:49 +00:00
let source = get_consumed ( input , remaining ) ;
Ok ( (
remaining ,
2023-04-21 21:40:49 +00:00
VerseBlock {
2023-08-23 04:30:26 +00:00
source : source . into ( ) ,
name : name . into ( ) ,
data : parameters . map ( | parameters | Into ::< & str > ::into ( parameters ) ) ,
2023-04-21 20:57:49 +00:00
children ,
} ,
) )
}
2023-08-11 00:04:59 +00:00
#[ cfg_attr(feature = " tracing " , tracing::instrument(ret, level = " debug " )) ]
2023-04-21 21:40:49 +00:00
pub fn comment_block < ' r , ' s > (
2023-09-02 23:16:44 +00:00
context : RefContext < ' r , ' s > ,
2023-08-23 04:30:26 +00:00
input : OrgSource < ' s > ,
) -> Res < OrgSource < ' s > , CommentBlock < ' s > > {
2023-04-21 21:32:53 +00:00
let ( remaining , name ) = lesser_block_begin ( " comment " ) ( context , input ) ? ;
let ( remaining , parameters ) = opt ( tuple ( ( space1 , data ) ) ) ( remaining ) ? ;
let ( remaining , _nl ) = line_ending ( remaining ) ? ;
let lesser_block_end_specialized = lesser_block_end ( " comment " ) ;
let parser_context = context
. with_additional_node ( ContextElement ::ConsumeTrailingWhitespace ( true ) )
. with_additional_node ( ContextElement ::Context ( " lesser block " ) )
. with_additional_node ( ContextElement ::ExitMatcherNode ( ExitMatcherNode {
class : ExitClass ::Beta ,
exit_matcher : & lesser_block_end_specialized ,
} ) ) ;
let parameters = match parameters {
Some ( ( _ws , parameters ) ) = > Some ( parameters ) ,
None = > None ,
} ;
2023-04-23 02:06:34 +00:00
let ( remaining , contents ) = parser_with_context! ( text_until_exit ) ( & parser_context ) ( remaining ) ? ;
2023-04-21 21:32:53 +00:00
let ( remaining , _end ) = lesser_block_end_specialized ( & parser_context , remaining ) ? ;
let source = get_consumed ( input , remaining ) ;
Ok ( (
remaining ,
2023-04-21 21:40:49 +00:00
CommentBlock {
2023-08-23 04:30:26 +00:00
source : source . into ( ) ,
name : name . into ( ) ,
data : parameters . map ( | parameters | Into ::< & str > ::into ( parameters ) ) ,
contents : contents . into ( ) ,
2023-04-21 21:40:49 +00:00
} ,
) )
}
2023-08-11 00:04:59 +00:00
#[ cfg_attr(feature = " tracing " , tracing::instrument(ret, level = " debug " )) ]
2023-04-21 21:40:49 +00:00
pub fn example_block < ' r , ' s > (
2023-09-02 23:16:44 +00:00
context : RefContext < ' r , ' s > ,
2023-08-23 04:30:26 +00:00
input : OrgSource < ' s > ,
) -> Res < OrgSource < ' s > , ExampleBlock < ' s > > {
2023-08-24 22:39:55 +00:00
let ( remaining , _name ) = lesser_block_begin ( " example " ) ( context , input ) ? ;
2023-04-21 21:40:49 +00:00
let ( remaining , parameters ) = opt ( tuple ( ( space1 , data ) ) ) ( remaining ) ? ;
let ( remaining , _nl ) = line_ending ( remaining ) ? ;
let lesser_block_end_specialized = lesser_block_end ( " example " ) ;
let parser_context = context
. with_additional_node ( ContextElement ::ConsumeTrailingWhitespace ( true ) )
. with_additional_node ( ContextElement ::Context ( " lesser block " ) )
. with_additional_node ( ContextElement ::ExitMatcherNode ( ExitMatcherNode {
class : ExitClass ::Beta ,
exit_matcher : & lesser_block_end_specialized ,
} ) ) ;
let parameters = match parameters {
Some ( ( _ws , parameters ) ) = > Some ( parameters ) ,
None = > None ,
} ;
2023-04-23 02:06:34 +00:00
let ( remaining , contents ) = parser_with_context! ( text_until_exit ) ( & parser_context ) ( remaining ) ? ;
2023-04-21 21:40:49 +00:00
let ( remaining , _end ) = lesser_block_end_specialized ( & parser_context , remaining ) ? ;
let source = get_consumed ( input , remaining ) ;
Ok ( (
remaining ,
ExampleBlock {
2023-08-23 04:30:26 +00:00
source : source . into ( ) ,
name : source . into ( ) ,
data : parameters . map ( | parameters | Into ::< & str > ::into ( parameters ) ) ,
contents : contents . into ( ) ,
2023-04-21 21:40:49 +00:00
} ,
) )
}
2023-08-11 00:04:59 +00:00
#[ cfg_attr(feature = " tracing " , tracing::instrument(ret, level = " debug " )) ]
2023-04-21 21:40:49 +00:00
pub fn export_block < ' r , ' s > (
2023-09-02 23:16:44 +00:00
context : RefContext < ' r , ' s > ,
2023-08-23 04:30:26 +00:00
input : OrgSource < ' s > ,
) -> Res < OrgSource < ' s > , ExportBlock < ' s > > {
2023-04-21 21:40:49 +00:00
let ( remaining , name ) = lesser_block_begin ( " export " ) ( context , input ) ? ;
2023-04-21 22:11:26 +00:00
// https://orgmode.org/worg/org-syntax.html#Blocks claims that export blocks must have a single word for data but testing shows no data and multi-word data still parses as an export block.
2023-04-21 21:40:49 +00:00
let ( remaining , parameters ) = opt ( tuple ( ( space1 , data ) ) ) ( remaining ) ? ;
let ( remaining , _nl ) = line_ending ( remaining ) ? ;
let lesser_block_end_specialized = lesser_block_end ( " export " ) ;
let parser_context = context
. with_additional_node ( ContextElement ::ConsumeTrailingWhitespace ( true ) )
. with_additional_node ( ContextElement ::Context ( " lesser block " ) )
. with_additional_node ( ContextElement ::ExitMatcherNode ( ExitMatcherNode {
class : ExitClass ::Beta ,
exit_matcher : & lesser_block_end_specialized ,
} ) ) ;
let parameters = match parameters {
Some ( ( _ws , parameters ) ) = > Some ( parameters ) ,
None = > None ,
} ;
2023-04-23 02:06:34 +00:00
let ( remaining , contents ) = parser_with_context! ( text_until_exit ) ( & parser_context ) ( remaining ) ? ;
2023-04-21 21:40:49 +00:00
let ( remaining , _end ) = lesser_block_end_specialized ( & parser_context , remaining ) ? ;
let source = get_consumed ( input , remaining ) ;
Ok ( (
remaining ,
ExportBlock {
2023-08-23 04:30:26 +00:00
source : source . into ( ) ,
name : name . into ( ) ,
data : parameters . map ( | parameters | Into ::< & str > ::into ( parameters ) ) ,
contents : contents . into ( ) ,
2023-04-21 21:40:49 +00:00
} ,
) )
}
2023-08-11 00:04:59 +00:00
#[ cfg_attr(feature = " tracing " , tracing::instrument(ret, level = " debug " )) ]
2023-08-23 04:30:26 +00:00
pub fn src_block < ' r , ' s > (
2023-09-02 23:16:44 +00:00
context : RefContext < ' r , ' s > ,
2023-08-23 04:30:26 +00:00
input : OrgSource < ' s > ,
) -> Res < OrgSource < ' s > , SrcBlock < ' s > > {
2023-04-21 21:40:49 +00:00
let ( remaining , name ) = lesser_block_begin ( " src " ) ( context , input ) ? ;
2023-04-21 22:13:23 +00:00
// https://orgmode.org/worg/org-syntax.html#Blocks claims that data is mandatory and must follow the LANGUAGE SWITCHES ARGUMENTS pattern but testing has shown that no data and incorrect data here will still parse to a src block.
2023-04-21 21:40:49 +00:00
let ( remaining , parameters ) = opt ( tuple ( ( space1 , data ) ) ) ( remaining ) ? ;
let ( remaining , _nl ) = line_ending ( remaining ) ? ;
let lesser_block_end_specialized = lesser_block_end ( " src " ) ;
let parser_context = context
. with_additional_node ( ContextElement ::ConsumeTrailingWhitespace ( true ) )
. with_additional_node ( ContextElement ::Context ( " lesser block " ) )
. with_additional_node ( ContextElement ::ExitMatcherNode ( ExitMatcherNode {
2023-08-14 21:28:57 +00:00
class : ExitClass ::Alpha ,
2023-04-21 21:40:49 +00:00
exit_matcher : & lesser_block_end_specialized ,
} ) ) ;
let parameters = match parameters {
Some ( ( _ws , parameters ) ) = > Some ( parameters ) ,
None = > None ,
} ;
2023-04-23 02:06:34 +00:00
let ( remaining , contents ) = parser_with_context! ( text_until_exit ) ( & parser_context ) ( remaining ) ? ;
2023-04-21 21:40:49 +00:00
let ( remaining , _end ) = lesser_block_end_specialized ( & parser_context , remaining ) ? ;
let source = get_consumed ( input , remaining ) ;
Ok ( (
remaining ,
SrcBlock {
2023-08-23 04:30:26 +00:00
source : source . into ( ) ,
name : name . into ( ) ,
data : parameters . map ( | parameters | Into ::< & str > ::into ( parameters ) ) ,
contents : contents . into ( ) ,
2023-04-21 21:32:53 +00:00
} ,
) )
}
2023-08-11 00:04:59 +00:00
#[ cfg_attr(feature = " tracing " , tracing::instrument(ret, level = " debug " )) ]
2023-08-23 04:30:26 +00:00
fn name < ' s > ( input : OrgSource < ' s > ) -> Res < OrgSource < ' s > , OrgSource < ' s > > {
2023-04-21 20:57:49 +00:00
is_not ( " \t \r \n " ) ( input )
}
2023-08-11 00:04:59 +00:00
#[ cfg_attr(feature = " tracing " , tracing::instrument(ret, level = " debug " )) ]
2023-08-23 04:30:26 +00:00
fn data < ' s > ( input : OrgSource < ' s > ) -> Res < OrgSource < ' s > , OrgSource < ' s > > {
2023-04-21 20:57:49 +00:00
is_not ( " \r \n " ) ( input )
}
2023-04-21 21:16:57 +00:00
fn lesser_block_end (
current_name : & str ,
2023-08-23 04:30:26 +00:00
) -> impl for < ' r , ' s > Fn ( Context < ' r , ' s > , OrgSource < ' s > ) -> Res < OrgSource < ' s > , OrgSource < ' s > > {
2023-04-21 21:16:57 +00:00
let current_name_lower = current_name . to_lowercase ( ) ;
2023-08-23 04:30:26 +00:00
move | context : Context , input : OrgSource < '_ > | {
2023-08-14 21:18:48 +00:00
_lesser_block_end ( context , input , current_name_lower . as_str ( ) )
2023-04-21 21:11:27 +00:00
}
2023-04-21 20:31:25 +00:00
}
2023-04-21 21:32:53 +00:00
2023-08-14 21:18:48 +00:00
#[ cfg_attr(feature = " tracing " , tracing::instrument(ret, level = " debug " )) ]
fn _lesser_block_end < ' r , ' s , ' x > (
2023-09-02 23:16:44 +00:00
_context : RefContext < ' r , ' s > ,
2023-08-23 04:30:26 +00:00
input : OrgSource < ' s > ,
2023-08-14 21:18:48 +00:00
current_name_lower : & ' x str ,
2023-08-23 04:30:26 +00:00
) -> Res < OrgSource < ' s > , OrgSource < ' s > > {
2023-08-24 23:29:00 +00:00
start_of_line ( input ) ? ;
2023-08-14 21:18:48 +00:00
let ( remaining , _leading_whitespace ) = space0 ( input ) ? ;
let ( remaining , ( _begin , _name , _ws ) ) = tuple ( (
tag_no_case ( " #+end_ " ) ,
tag_no_case ( current_name_lower ) ,
alt ( ( eof , line_ending ) ) ,
) ) ( remaining ) ? ;
let source = get_consumed ( input , remaining ) ;
Ok ( ( remaining , source ) )
}
2023-08-25 00:10:43 +00:00
/// Parser for the beginning of a lesser block
///
/// current_name MUST be lowercase. We do not do the conversion ourselves because it is not allowed in a const fn.
const fn lesser_block_begin (
current_name : & 'static str ,
2023-08-23 04:30:26 +00:00
) -> impl for < ' r , ' s > Fn ( Context < ' r , ' s > , OrgSource < ' s > ) -> Res < OrgSource < ' s > , OrgSource < ' s > > {
2023-08-25 00:10:43 +00:00
// TODO: Since this is a const fn, is there ANY way to "generate" functions at compile time?
move | context : Context , input : OrgSource < '_ > | _lesser_block_begin ( context , input , current_name )
2023-04-21 21:32:53 +00:00
}
2023-08-14 21:18:48 +00:00
#[ cfg_attr(feature = " tracing " , tracing::instrument(ret, level = " debug " )) ]
fn _lesser_block_begin < ' r , ' s , ' x > (
2023-09-02 23:16:44 +00:00
_context : RefContext < ' r , ' s > ,
2023-08-23 04:30:26 +00:00
input : OrgSource < ' s > ,
2023-08-14 21:18:48 +00:00
current_name_lower : & ' x str ,
2023-08-23 04:30:26 +00:00
) -> Res < OrgSource < ' s > , OrgSource < ' s > > {
2023-08-24 23:29:00 +00:00
start_of_line ( input ) ? ;
2023-08-14 21:18:48 +00:00
let ( remaining , _leading_whitespace ) = space0 ( input ) ? ;
let ( remaining , ( _begin , name ) ) = tuple ( (
tag_no_case ( " #+begin_ " ) ,
2023-08-23 04:30:26 +00:00
verify ( name , | name : & OrgSource < '_ > | {
Into ::< & str > ::into ( name ) . to_lowercase ( ) . as_str ( ) = = current_name_lower
2023-08-14 21:18:48 +00:00
} ) ,
) ) ( remaining ) ? ;
Ok ( ( remaining , name ) )
}