2023-04-23 01:45:18 +00:00
use nom ::branch ::alt ;
use nom ::bytes ::complete ::tag ;
use nom ::bytes ::complete ::tag_no_case ;
use nom ::bytes ::complete ::take_while ;
use nom ::character ::complete ::digit1 ;
use nom ::character ::complete ::space0 ;
2023-09-09 01:11:47 +00:00
use nom ::combinator ::opt ;
2023-04-23 01:45:18 +00:00
use nom ::combinator ::recognize ;
use nom ::combinator ::verify ;
2023-09-09 01:11:47 +00:00
use nom ::multi ::many0 ;
2023-04-23 01:45:18 +00:00
use nom ::multi ::many1 ;
use nom ::multi ::many_till ;
use nom ::sequence ::tuple ;
2023-08-23 04:30:26 +00:00
use super ::org_source ::OrgSource ;
2023-09-09 01:30:03 +00:00
use super ::util ::include_input ;
2023-04-07 21:14:44 +00:00
use super ::util ::WORD_CONSTITUENT_CHARACTERS ;
2023-09-03 16:07:51 +00:00
use crate ::context ::parser_with_context ;
use crate ::context ::ContextElement ;
use crate ::context ::ExitClass ;
use crate ::context ::ExitMatcherNode ;
use crate ::context ::RefContext ;
2023-04-21 22:36:01 +00:00
use crate ::error ::CustomError ;
use crate ::error ::MyError ;
use crate ::error ::Res ;
2023-04-21 20:10:56 +00:00
use crate ::parser ::element_parser ::element ;
2023-04-07 21:14:44 +00:00
use crate ::parser ::util ::blank_line ;
use crate ::parser ::util ::exit_matcher_parser ;
use crate ::parser ::util ::get_consumed ;
2023-04-19 02:10:44 +00:00
use crate ::parser ::util ::immediate_in_section ;
2023-04-10 15:50:43 +00:00
use crate ::parser ::util ::maybe_consume_trailing_whitespace ;
2023-04-07 21:14:44 +00:00
use crate ::parser ::util ::start_of_line ;
2023-09-03 16:07:51 +00:00
use crate ::types ::FootnoteDefinition ;
2023-04-07 21:14:44 +00:00
2023-08-11 00:04:59 +00:00
#[ cfg_attr(feature = " tracing " , tracing::instrument(ret, level = " debug " )) ]
2023-09-11 17:13:28 +00:00
pub ( crate ) fn footnote_definition < ' b , ' g , ' r , ' s > (
2023-09-03 19:44:18 +00:00
context : RefContext < ' b , ' g , ' r , ' s > ,
2023-08-23 04:30:26 +00:00
input : OrgSource < ' s > ,
) -> Res < OrgSource < ' s > , FootnoteDefinition < ' s > > {
2023-04-19 02:10:44 +00:00
if immediate_in_section ( context , " footnote definition " ) {
return Err ( nom ::Err ::Error ( CustomError ::MyError ( MyError (
2023-08-23 04:30:26 +00:00
" Cannot nest objects of the same element " . into ( ) ,
2023-04-19 02:10:44 +00:00
) ) ) ) ;
}
2023-08-24 23:29:00 +00:00
start_of_line ( input ) ? ;
2023-04-07 21:14:44 +00:00
// Cannot be indented.
2023-09-09 01:11:47 +00:00
let ( remaining , ( _ , lbl , _ , _ , _ ) ) = tuple ( (
tag_no_case ( " [fn: " ) ,
label ,
tag ( " ] " ) ,
space0 ,
opt ( verify ( many0 ( blank_line ) , | lines : & Vec < OrgSource < '_ > > | {
lines . len ( ) < = 2
} ) ) ,
) ) ( input ) ? ;
2023-09-03 16:07:51 +00:00
let contexts = [
ContextElement ::ConsumeTrailingWhitespace ( true ) ,
ContextElement ::Context ( " footnote definition " ) ,
ContextElement ::ExitMatcherNode ( ExitMatcherNode {
2023-04-19 00:33:01 +00:00
class : ExitClass ::Alpha ,
exit_matcher : & footnote_definition_end ,
2023-09-03 16:07:51 +00:00
} ) ,
] ;
2023-09-03 16:28:45 +00:00
let parser_context = context . with_additional_node ( & contexts [ 0 ] ) ;
let parser_context = parser_context . with_additional_node ( & contexts [ 1 ] ) ;
let parser_context = parser_context . with_additional_node ( & contexts [ 2 ] ) ;
2023-04-22 05:45:38 +00:00
let element_matcher = parser_with_context! ( element ( true ) ) ( & parser_context ) ;
2023-04-07 21:14:44 +00:00
let exit_matcher = parser_with_context! ( exit_matcher_parser ) ( & parser_context ) ;
2023-09-09 01:30:03 +00:00
let ( mut remaining , ( mut children , _exit_contents ) ) =
many_till ( include_input ( element_matcher ) , exit_matcher ) ( remaining ) ? ;
// Re-parse the last element of the footnote definition with consume trailing whitespace off because the trailing whitespace needs to belong to the footnote definition, not the contents.
if context . should_consume_trailing_whitespace ( ) {
if let Some ( ( final_item_input , _ ) ) = children . pop ( ) {
let final_item_context = ContextElement ::ConsumeTrailingWhitespace ( false ) ;
let final_item_context = parser_context . with_additional_node ( & final_item_context ) ;
let ( remain , reparsed_final_item ) =
parser_with_context! ( element ( true ) ) ( & final_item_context ) ( final_item_input ) ? ;
children . push ( ( final_item_input , reparsed_final_item ) ) ;
remaining = remain ;
}
}
2023-04-10 15:50:43 +00:00
2023-04-07 21:14:44 +00:00
let source = get_consumed ( input , remaining ) ;
Ok ( (
remaining ,
FootnoteDefinition {
2023-08-23 04:30:26 +00:00
source : source . into ( ) ,
label : lbl . into ( ) ,
2023-09-09 01:30:03 +00:00
children : children . into_iter ( ) . map ( | ( _ , item ) | item ) . collect ( ) ,
2023-04-07 21:14:44 +00:00
} ,
) )
}
2023-08-11 00:04:59 +00:00
#[ cfg_attr(feature = " tracing " , tracing::instrument(ret, level = " debug " )) ]
2023-09-11 17:13:28 +00:00
pub ( crate ) fn label < ' s > ( input : OrgSource < ' s > ) -> Res < OrgSource < ' s > , OrgSource < ' s > > {
2023-04-07 21:14:44 +00:00
alt ( (
digit1 ,
take_while ( | c | WORD_CONSTITUENT_CHARACTERS . contains ( c ) | | " -_ " . contains ( c ) ) ,
) ) ( input )
}
2023-08-11 00:04:59 +00:00
#[ cfg_attr(feature = " tracing " , tracing::instrument(ret, level = " debug " )) ]
2023-09-03 19:44:18 +00:00
fn footnote_definition_end < ' b , ' g , ' r , ' s > (
context : RefContext < ' b , ' g , ' r , ' s > ,
2023-08-23 04:30:26 +00:00
input : OrgSource < ' s > ,
) -> Res < OrgSource < ' s > , OrgSource < ' s > > {
2023-04-19 18:30:02 +00:00
let ( remaining , source ) = alt ( (
2023-04-10 16:03:01 +00:00
recognize ( tuple ( (
2023-08-31 22:46:31 +00:00
parser_with_context! ( maybe_consume_trailing_whitespace ) ( context ) ,
detect_footnote_definition ,
2023-04-10 16:03:01 +00:00
) ) ) ,
2023-04-10 13:49:24 +00:00
recognize ( tuple ( (
2023-08-24 23:29:00 +00:00
start_of_line ,
2023-08-23 04:30:26 +00:00
verify ( many1 ( blank_line ) , | lines : & Vec < OrgSource < '_ > > | {
lines . len ( ) > = 2
} ) ,
2023-04-10 13:49:24 +00:00
) ) ) ,
2023-04-19 18:30:02 +00:00
) ) ( input ) ? ;
Ok ( ( remaining , source ) )
2023-04-07 21:14:44 +00:00
}
2023-04-10 15:54:05 +00:00
2023-08-31 22:46:31 +00:00
#[ cfg_attr(feature = " tracing " , tracing::instrument(ret, level = " debug " )) ]
2023-09-11 17:13:28 +00:00
pub ( crate ) fn detect_footnote_definition < ' s > ( input : OrgSource < ' s > ) -> Res < OrgSource < ' s > , ( ) > {
2023-08-31 22:46:31 +00:00
tuple ( ( start_of_line , tag_no_case ( " [fn: " ) , label , tag ( " ] " ) ) ) ( input ) ? ;
Ok ( ( input , ( ) ) )
}
2023-04-10 15:54:05 +00:00
#[ cfg(test) ]
mod tests {
2023-04-23 01:45:18 +00:00
use super ::* ;
2023-09-03 16:07:51 +00:00
use crate ::context ::Context ;
use crate ::context ::GlobalSettings ;
use crate ::context ::List ;
2023-09-03 16:28:45 +00:00
use crate ::types ::Source ;
2023-04-10 15:54:05 +00:00
#[ test ]
fn two_paragraphs ( ) {
2023-08-24 21:15:24 +00:00
let input = OrgSource ::new (
" [fn:1] A footnote.
2023-04-10 15:54:05 +00:00
[ fn :2 ] A multi -
2023-08-24 21:15:24 +00:00
line footnote . " ,
) ;
2023-09-03 16:07:51 +00:00
let global_settings = GlobalSettings ::default ( ) ;
let initial_context = ContextElement ::document_context ( ) ;
let initial_context = Context ::new ( & global_settings , List ::new ( & initial_context ) ) ;
2023-08-24 21:15:24 +00:00
let footnote_definition_matcher = parser_with_context! ( element ( true ) ) ( & initial_context ) ;
2023-04-10 15:54:05 +00:00
let ( remaining , first_footnote_definition ) =
footnote_definition_matcher ( input ) . expect ( " Parse first footnote_definition " ) ;
let ( remaining , second_footnote_definition ) =
footnote_definition_matcher ( remaining ) . expect ( " Parse second footnote_definition. " ) ;
2023-08-24 21:15:24 +00:00
assert_eq! ( Into ::< & str > ::into ( remaining ) , " " ) ;
2023-04-10 15:54:05 +00:00
assert_eq! (
2023-04-22 04:19:01 +00:00
first_footnote_definition . get_source ( ) ,
2023-04-10 15:54:05 +00:00
" [fn:1] A footnote.
"
) ;
assert_eq! (
2023-04-22 04:19:01 +00:00
second_footnote_definition . get_source ( ) ,
2023-04-10 15:54:05 +00:00
" [fn:2] A multi-
line footnote . "
) ;
}
2023-04-10 16:16:49 +00:00
#[ test ]
fn multiline_break ( ) {
2023-08-24 21:15:24 +00:00
let input = OrgSource ::new (
" [fn:2] A multi-
2023-04-10 16:16:49 +00:00
line footnote .
2023-08-24 21:15:24 +00:00
not in the footnote . " ,
) ;
2023-09-03 16:07:51 +00:00
let global_settings = GlobalSettings ::default ( ) ;
let initial_context = ContextElement ::document_context ( ) ;
let initial_context = Context ::new ( & global_settings , List ::new ( & initial_context ) ) ;
2023-08-24 21:15:24 +00:00
let footnote_definition_matcher = parser_with_context! ( element ( true ) ) ( & initial_context ) ;
2023-04-10 16:16:49 +00:00
let ( remaining , first_footnote_definition ) =
footnote_definition_matcher ( input ) . expect ( " Parse first footnote_definition " ) ;
2023-08-24 21:15:24 +00:00
assert_eq! ( Into ::< & str > ::into ( remaining ) , " not in the footnote. " ) ;
2023-04-10 16:16:49 +00:00
assert_eq! (
2023-04-22 04:19:01 +00:00
first_footnote_definition . get_source ( ) ,
2023-04-10 16:16:49 +00:00
" [fn:2] A multi-
line footnote .
"
) ;
}
2023-04-10 15:54:05 +00:00
}