2023-03-23 23:35:32 +00:00
use nom ::branch ::alt ;
use nom ::bytes ::complete ::tag ;
2023-08-25 10:13:29 +00:00
use nom ::character ::complete ::anychar ;
2023-03-23 23:53:20 +00:00
use nom ::character ::complete ::line_ending ;
2023-08-25 10:13:29 +00:00
use nom ::character ::complete ::space0 ;
2023-03-23 23:53:20 +00:00
use nom ::character ::complete ::space1 ;
2023-09-03 19:44:18 +00:00
use nom ::combinator ::all_consuming ;
2023-04-19 23:53:37 +00:00
use nom ::combinator ::eof ;
2023-03-24 21:19:46 +00:00
use nom ::combinator ::map ;
2023-03-24 00:12:42 +00:00
use nom ::combinator ::not ;
2023-03-24 21:34:56 +00:00
use nom ::combinator ::opt ;
2023-03-23 23:35:32 +00:00
use nom ::combinator ::recognize ;
2023-03-24 21:19:46 +00:00
use nom ::combinator ::verify ;
use nom ::multi ::many0 ;
2023-03-24 00:00:35 +00:00
use nom ::multi ::many1 ;
2023-03-23 23:35:32 +00:00
use nom ::multi ::many1_count ;
2023-03-27 17:06:41 +00:00
use nom ::multi ::many_till ;
2023-08-25 10:13:29 +00:00
use nom ::multi ::separated_list1 ;
2023-03-23 23:35:32 +00:00
use nom ::sequence ::tuple ;
2023-09-04 23:17:23 +00:00
use super ::in_buffer_settings ::scan_for_in_buffer_settings ;
2023-08-23 02:33:50 +00:00
use super ::org_source ::OrgSource ;
2023-07-15 00:45:31 +00:00
use super ::token ::AllTokensIterator ;
2023-04-25 02:10:24 +00:00
use super ::token ::Token ;
2023-03-27 17:06:41 +00:00
use super ::util ::exit_matcher_parser ;
2023-03-25 15:25:10 +00:00
use super ::util ::get_consumed ;
2023-03-25 18:10:22 +00:00
use super ::util ::start_of_line ;
2023-09-03 04:05:47 +00:00
use crate ::context ::parser_with_context ;
use crate ::context ::Context ;
use crate ::context ::ContextElement ;
use crate ::context ::ExitClass ;
use crate ::context ::ExitMatcherNode ;
use crate ::context ::GlobalSettings ;
use crate ::context ::List ;
use crate ::context ::RefContext ;
2023-09-04 20:53:02 +00:00
use crate ::error ::CustomError ;
2023-09-04 21:16:08 +00:00
use crate ::error ::MyError ;
2023-04-23 01:45:18 +00:00
use crate ::error ::Res ;
use crate ::parser ::comment ::comment ;
use crate ::parser ::element_parser ::element ;
use crate ::parser ::object_parser ::standard_set_object ;
2023-09-04 15:36:42 +00:00
use crate ::parser ::org_source ::convert_error ;
2023-04-23 01:45:18 +00:00
use crate ::parser ::planning ::planning ;
use crate ::parser ::property_drawer ::property_drawer ;
use crate ::parser ::util ::blank_line ;
use crate ::parser ::util ::maybe_consume_trailing_whitespace_if_not_exiting ;
2023-09-03 04:05:47 +00:00
use crate ::types ::Document ;
use crate ::types ::DocumentElement ;
use crate ::types ::Element ;
use crate ::types ::Heading ;
use crate ::types ::Object ;
use crate ::types ::Section ;
2022-10-15 00:17:48 +00:00
2023-09-04 21:38:02 +00:00
/// Parse a full org-mode document.
///
/// This is the main entry point for Organic. It will parse the full contents of the input string as an org-mode document.
2023-08-11 00:04:59 +00:00
#[ cfg_attr(feature = " tracing " , tracing::instrument(ret, level = " debug " )) ]
2023-03-24 00:12:42 +00:00
#[ allow(dead_code) ]
2023-09-03 16:58:46 +00:00
pub fn parse < ' s > ( input : & ' s str ) -> Result < Document < ' s > , String > {
2023-09-03 04:05:47 +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-23 02:33:50 +00:00
let wrapped_input = OrgSource ::new ( input ) ;
2023-09-04 15:36:42 +00:00
let ret =
all_consuming ( parser_with_context! ( document_org_source ) ( & initial_context ) ) ( wrapped_input )
. map_err ( | err | err . to_string ( ) )
. map ( | ( _remaining , parsed_document ) | parsed_document ) ;
2023-09-03 16:58:46 +00:00
ret
2023-09-03 16:45:12 +00:00
}
2023-09-04 21:38:02 +00:00
/// Parse a full org-mode document with starting settings.
///
/// This is the secondary entry point for Organic. It will parse the full contents of the input string as an org-mode document starting with the settings you supplied.
///
/// This will not prevent additional settings from being learned during parsing, for example when encountering a "#+TODO".
#[ cfg_attr(feature = " tracing " , tracing::instrument(ret, level = " debug " )) ]
#[ allow(dead_code) ]
pub fn parse_with_settings < ' g , ' s > (
input : & ' s str ,
global_settings : & ' g GlobalSettings < ' g , ' s > ,
) -> Result < Document < ' s > , String > {
let initial_context = ContextElement ::document_context ( ) ;
let initial_context = Context ::new ( global_settings , List ::new ( & initial_context ) ) ;
let wrapped_input = OrgSource ::new ( input ) ;
let ret =
all_consuming ( parser_with_context! ( document_org_source ) ( & initial_context ) ) ( wrapped_input )
. map_err ( | err | err . to_string ( ) )
. map ( | ( _remaining , parsed_document ) | parsed_document ) ;
ret
}
/// Parse a full org-mode document.
///
/// Use this entry point when you want to have direct control over the starting context or if you want to use this integrated with other nom parsers. For general-purpose usage, the `parse` and `parse_with_settings` functions are a lot simpler.
2023-09-04 23:17:23 +00:00
///
/// This will not prevent additional settings from being learned during parsing, for example when encountering a "#+TODO".
2023-09-03 16:45:12 +00:00
#[ cfg_attr(feature = " tracing " , tracing::instrument(ret, level = " debug " )) ]
#[ allow(dead_code) ]
2023-09-03 19:44:18 +00:00
pub fn document < ' b , ' g , ' r , ' s > (
2023-09-04 15:36:42 +00:00
context : RefContext < ' b , ' g , ' r , ' s > ,
input : & ' s str ,
) -> Res < & ' s str , Document < ' s > > {
let ( remaining , doc ) = document_org_source ( context , input . into ( ) ) . map_err ( convert_error ) ? ;
Ok ( ( Into ::< & str > ::into ( remaining ) , doc ) )
}
#[ cfg_attr(feature = " tracing " , tracing::instrument(ret, level = " debug " )) ]
#[ allow(dead_code) ]
fn document_org_source < ' b , ' g , ' r , ' s > (
2023-09-03 19:44:18 +00:00
context : RefContext < ' b , ' g , ' r , ' s > ,
2023-09-03 16:45:12 +00:00
input : OrgSource < ' s > ,
) -> Res < OrgSource < ' s > , Document < ' s > > {
2023-09-05 02:05:43 +00:00
let mut final_settings = Vec ::new ( ) ;
2023-09-04 23:17:23 +00:00
let ( _ , document_settings ) = scan_for_in_buffer_settings ( input ) ? ;
2023-09-05 02:05:43 +00:00
let setup_files : Vec < String > = document_settings
. iter ( )
. filter ( | kw | kw . key . eq_ignore_ascii_case ( " setupfile " ) )
. map ( | kw | kw . value )
. map ( | setup_file | {
context
. get_global_settings ( )
. file_access
. read_file ( setup_file )
. map_err ( | err | nom ::Err ::< CustomError < OrgSource < '_ > > > ::Failure ( err . into ( ) ) )
} )
. collect ::< Result < Vec < _ > , _ > > ( ) ? ;
for setup_file in setup_files . iter ( ) . map ( String ::as_str ) {
let ( _ , setup_file_settings ) =
scan_for_in_buffer_settings ( setup_file . into ( ) ) . map_err ( | _err | {
nom ::Err ::Error ( CustomError ::MyError ( MyError (
" TODO: make this take an owned string so I can dump err.to_string() into it. "
. into ( ) ,
) ) )
} ) ? ;
final_settings . extend ( setup_file_settings ) ;
2023-09-04 16:48:59 +00:00
}
2023-09-05 02:05:43 +00:00
final_settings . extend ( document_settings ) ;
2023-09-04 23:17:23 +00:00
// TODO: read the keywords into settings and apply them to the GlobalSettings.
2023-09-03 16:45:12 +00:00
let ( remaining , document ) =
_document ( context , input ) . map ( | ( rem , out ) | ( Into ::< & str > ::into ( rem ) , out ) ) ? ;
2023-07-14 23:06:58 +00:00
{
// If there are radio targets in this document then we need to parse the entire document again with the knowledge of the radio targets.
let all_radio_targets : Vec < & Vec < Object < '_ > > > = document
. iter_tokens ( )
. filter_map ( | tkn | match tkn {
Token ::Object ( obj ) = > Some ( obj ) ,
_ = > None ,
} )
. filter_map ( | obj | match obj {
Object ::RadioTarget ( rt ) = > Some ( rt ) ,
_ = > None ,
} )
. map ( | rt | & rt . children )
. collect ( ) ;
if ! all_radio_targets . is_empty ( ) {
2023-09-04 16:28:33 +00:00
let mut new_global_settings = context . get_global_settings ( ) . clone ( ) ;
new_global_settings . radio_targets = all_radio_targets ;
let parser_context = context . with_global_settings ( & new_global_settings ) ;
2023-09-03 16:45:12 +00:00
let ( remaining , document ) = _document ( & parser_context , input )
. map ( | ( rem , out ) | ( Into ::< & str > ::into ( rem ) , out ) ) ? ;
2023-08-23 04:30:26 +00:00
return Ok ( ( remaining . into ( ) , document ) ) ;
2023-07-14 23:06:58 +00:00
}
}
2023-08-23 04:30:26 +00:00
Ok ( ( remaining . into ( ) , document ) )
2023-07-14 23:06:58 +00:00
}
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 _document < ' b , ' g , ' r , ' s > (
context : RefContext < ' b , ' g , ' r , ' s > ,
2023-08-23 04:30:26 +00:00
input : OrgSource < ' s > ,
) -> Res < OrgSource < ' s > , Document < ' s > > {
2023-07-14 23:06:58 +00:00
let zeroth_section_matcher = parser_with_context! ( zeroth_section ) ( context ) ;
2023-08-29 17:48:10 +00:00
let heading_matcher = parser_with_context! ( heading ( 0 ) ) ( context ) ;
2023-04-19 22:37:39 +00:00
let ( remaining , _blank_lines ) = many0 ( blank_line ) ( input ) ? ;
let ( remaining , zeroth_section ) = opt ( zeroth_section_matcher ) ( remaining ) ? ;
2023-03-27 22:08:17 +00:00
let ( remaining , children ) = many0 ( heading_matcher ) ( remaining ) ? ;
2023-03-24 21:34:56 +00:00
let source = get_consumed ( input , remaining ) ;
Ok ( (
remaining ,
Document {
2023-08-23 04:30:26 +00:00
source : source . into ( ) ,
2023-03-24 21:34:56 +00:00
zeroth_section ,
children ,
} ,
) )
2023-03-23 21:59:39 +00:00
}
2023-03-23 23:35:32 +00:00
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 zeroth_section < ' b , ' g , ' r , ' s > (
context : RefContext < ' b , ' g , ' r , ' s > ,
2023-08-23 04:30:26 +00:00
input : OrgSource < ' s > ,
) -> Res < OrgSource < ' s > , Section < ' s > > {
2023-04-19 22:37:39 +00:00
// TODO: The zeroth section is specialized so it probably needs its own parser
2023-09-03 04:05:47 +00:00
let contexts = [
ContextElement ::ConsumeTrailingWhitespace ( true ) ,
ContextElement ::Context ( " section " ) ,
ContextElement ::ExitMatcherNode ( ExitMatcherNode {
2023-04-19 22:37:39 +00:00
class : ExitClass ::Document ,
exit_matcher : & section_end ,
2023-09-03 04:05:47 +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-09-03 04:05:47 +00:00
let without_consuming_whitespace_context = ContextElement ::ConsumeTrailingWhitespace ( false ) ;
2023-04-19 23:03:51 +00:00
let without_consuming_whitespace_context =
2023-09-03 04:05:47 +00:00
parser_context . with_additional_node ( & without_consuming_whitespace_context ) ;
2023-04-19 23:03:51 +00:00
2023-04-22 05:45:38 +00:00
let element_matcher = parser_with_context! ( element ( true ) ) ( & parser_context ) ;
2023-04-19 22:37:39 +00:00
let exit_matcher = parser_with_context! ( exit_matcher_parser ) ( & parser_context ) ;
2023-04-19 23:22:23 +00:00
let ( remaining , comment_and_property_drawer_element ) = opt ( tuple ( (
2023-04-19 23:03:51 +00:00
opt ( parser_with_context! ( comment ) (
& without_consuming_whitespace_context ,
) ) ,
2023-08-14 15:57:12 +00:00
parser_with_context! ( property_drawer ) ( context ) ,
2023-04-19 23:28:21 +00:00
many0 ( blank_line ) ,
2023-04-19 23:22:23 +00:00
) ) ) ( input ) ? ;
2023-04-19 22:37:39 +00:00
let ( remaining , ( mut children , _exit_contents ) ) = verify (
many_till ( element_matcher , exit_matcher ) ,
2023-04-19 23:03:51 +00:00
| ( children , _exit_contents ) | {
2023-04-19 23:22:23 +00:00
! children . is_empty ( ) | | comment_and_property_drawer_element . is_some ( )
2023-04-19 23:03:51 +00:00
} ,
2023-04-19 22:37:39 +00:00
) ( remaining ) ? ;
2023-04-19 23:22:23 +00:00
2023-04-19 23:28:21 +00:00
comment_and_property_drawer_element . map ( | ( comment , property_drawer , _ws ) | {
2023-04-19 23:22:23 +00:00
children . insert ( 0 , Element ::PropertyDrawer ( property_drawer ) ) ;
comment
. map ( Element ::Comment )
. map ( | ele | children . insert ( 0 , ele ) ) ;
} ) ;
2023-04-19 22:37:39 +00:00
let ( remaining , _trailing_ws ) =
maybe_consume_trailing_whitespace_if_not_exiting ( context , remaining ) ? ;
let source = get_consumed ( input , remaining ) ;
2023-08-23 04:30:26 +00:00
Ok ( (
remaining ,
Section {
source : source . into ( ) ,
children ,
} ,
) )
2023-04-19 22:37:39 +00:00
}
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 section < ' b , ' g , ' r , ' s > (
context : RefContext < ' b , ' g , ' r , ' s > ,
2023-08-23 04:30:26 +00:00
mut input : OrgSource < ' s > ,
) -> Res < OrgSource < ' s > , Section < ' s > > {
2023-03-23 23:35:32 +00:00
// TODO: The zeroth section is specialized so it probably needs its own parser
2023-09-03 04:05:47 +00:00
let contexts = [
ContextElement ::ConsumeTrailingWhitespace ( true ) ,
ContextElement ::Context ( " section " ) ,
ContextElement ::ExitMatcherNode ( ExitMatcherNode {
2023-04-19 00:33:01 +00:00
class : ExitClass ::Document ,
exit_matcher : & section_end ,
2023-09-03 04:05:47 +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-03-27 17:06:41 +00:00
let exit_matcher = parser_with_context! ( exit_matcher_parser ) ( & parser_context ) ;
2023-04-22 01:39:26 +00:00
let ( mut remaining , ( planning_element , property_drawer_element ) ) = tuple ( (
opt ( parser_with_context! ( planning ) ( & parser_context ) ) ,
opt ( parser_with_context! ( property_drawer ) ( & parser_context ) ) ,
) ) ( input ) ? ;
if planning_element . is_none ( ) & & property_drawer_element . is_none ( ) {
2023-04-19 23:39:56 +00:00
let ( remain , _ws ) = many0 ( blank_line ) ( remaining ) ? ;
remaining = remain ;
input = remain ;
}
2023-04-19 23:03:51 +00:00
let ( remaining , ( mut children , _exit_contents ) ) = verify (
2023-04-10 15:16:32 +00:00
many_till ( element_matcher , exit_matcher ) ,
2023-04-22 01:39:26 +00:00
| ( children , _exit_contents ) | {
! children . is_empty ( ) | | property_drawer_element . is_some ( ) | | planning_element . is_some ( )
} ,
2023-04-19 23:03:51 +00:00
) ( remaining ) ? ;
property_drawer_element
. map ( Element ::PropertyDrawer )
. map ( | ele | children . insert ( 0 , ele ) ) ;
2023-04-22 01:39:26 +00:00
planning_element
. map ( Element ::Planning )
. map ( | ele | children . insert ( 0 , ele ) ) ;
2023-04-10 15:16:32 +00:00
2023-04-19 00:33:01 +00:00
let ( remaining , _trailing_ws ) =
maybe_consume_trailing_whitespace_if_not_exiting ( context , remaining ) ? ;
2023-04-10 15:16:32 +00:00
2023-03-24 21:00:27 +00:00
let source = get_consumed ( input , remaining ) ;
2023-08-23 04:30:26 +00:00
Ok ( (
remaining ,
Section {
source : source . into ( ) ,
children ,
} ,
) )
2023-03-23 23:35:32 +00:00
}
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 section_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-08-29 15:35:54 +00:00
recognize ( detect_headline ) ( input )
2023-03-23 23:35:32 +00:00
}
2023-08-29 17:48:10 +00:00
const fn heading (
parent_stars : usize ,
2023-09-03 19:44:18 +00:00
) -> impl for < ' b , ' g , ' r , ' s > Fn (
RefContext < ' b , ' g , ' r , ' s > ,
OrgSource < ' s > ,
) -> Res < OrgSource < ' s > , Heading < ' s > > {
2023-09-03 04:05:47 +00:00
move | context , input : OrgSource < '_ > | _heading ( context , input , parent_stars )
2023-08-29 17:48:10 +00:00
}
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 _heading < ' b , ' g , ' r , ' s > (
context : RefContext < ' b , ' g , ' r , ' s > ,
2023-08-23 04:30:26 +00:00
input : OrgSource < ' s > ,
2023-08-29 17:48:10 +00:00
parent_stars : usize ,
2023-08-23 04:30:26 +00:00
) -> Res < OrgSource < ' s > , Heading < ' s > > {
2023-03-24 00:12:42 +00:00
not ( | i | context . check_exit_matcher ( i ) ) ( input ) ? ;
2023-08-27 19:56:08 +00:00
let ( remaining , ( star_count , _ws , maybe_todo_keyword , title , heading_tags ) ) =
2023-08-29 17:48:10 +00:00
headline ( context , input , parent_stars ) ? ;
2023-03-24 21:19:46 +00:00
let section_matcher = parser_with_context! ( section ) ( context ) ;
2023-08-29 17:48:10 +00:00
let heading_matcher = parser_with_context! ( heading ( star_count ) ) ( context ) ;
2023-08-29 20:03:13 +00:00
let ( remaining , maybe_section ) =
opt ( map ( section_matcher , DocumentElement ::Section ) ) ( remaining ) ? ;
let ( remaining , mut children ) =
many0 ( map ( heading_matcher , DocumentElement ::Heading ) ) ( remaining ) ? ;
if let Some ( section ) = maybe_section {
children . insert ( 0 , section ) ;
}
2023-08-29 20:07:43 +00:00
let remaining = if children . is_empty ( ) {
// Support empty headings
let ( remain , _ws ) = many0 ( blank_line ) ( remaining ) ? ;
remain
} else {
remaining
} ;
2023-08-29 20:03:13 +00:00
2023-03-24 21:19:46 +00:00
let source = get_consumed ( input , remaining ) ;
Ok ( (
remaining ,
Heading {
2023-08-23 04:30:26 +00:00
source : source . into ( ) ,
2023-03-24 21:19:46 +00:00
stars : star_count ,
2023-08-27 19:56:08 +00:00
todo_keyword : maybe_todo_keyword
. map ( | ( todo_keyword , _ws ) | Into ::< & str > ::into ( todo_keyword ) ) ,
2023-03-25 16:18:47 +00:00
title ,
2023-08-25 10:20:06 +00:00
tags : heading_tags ,
2023-03-24 21:19:46 +00:00
children ,
} ,
) )
2023-03-23 23:53:20 +00:00
}
2023-08-29 15:35:54 +00:00
#[ cfg_attr(feature = " tracing " , tracing::instrument(ret, level = " debug " )) ]
fn detect_headline < ' s > ( input : OrgSource < ' s > ) -> Res < OrgSource < ' s > , ( ) > {
tuple ( ( start_of_line , many1 ( tag ( " * " ) ) , space1 ) ) ( input ) ? ;
Ok ( ( 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 headline < ' b , ' g , ' r , ' s > (
context : RefContext < ' b , ' g , ' r , ' s > ,
2023-08-23 04:30:26 +00:00
input : OrgSource < ' s > ,
2023-08-29 17:48:10 +00:00
parent_stars : usize ,
2023-08-27 19:56:08 +00:00
) -> Res <
OrgSource < ' s > ,
(
usize ,
OrgSource < ' s > ,
Option < ( OrgSource < ' s > , OrgSource < ' s > ) > ,
Vec < Object < ' s > > ,
Vec < & ' s str > ,
) ,
> {
2023-09-03 04:05:47 +00:00
let parser_context = ContextElement ::ExitMatcherNode ( ExitMatcherNode {
class : ExitClass ::Document ,
exit_matcher : & headline_title_end ,
} ) ;
let parser_context = context . with_additional_node ( & parser_context ) ;
2023-03-24 00:00:35 +00:00
let standard_set_object_matcher = parser_with_context! ( standard_set_object ) ( & parser_context ) ;
2023-03-23 23:53:20 +00:00
2023-08-27 19:56:08 +00:00
let (
remaining ,
( _sol , star_count , ws , maybe_todo_keyword , title , maybe_tags , _ws , _line_ending ) ,
) = tuple ( (
2023-08-24 23:29:00 +00:00
start_of_line ,
2023-08-29 17:48:10 +00:00
verify ( many1_count ( tag ( " * " ) ) , | star_count | {
* star_count > parent_stars
} ) ,
2023-03-23 23:53:20 +00:00
space1 ,
2023-08-27 19:56:08 +00:00
opt ( tuple ( ( heading_keyword , space1 ) ) ) ,
2023-03-24 00:00:35 +00:00
many1 ( standard_set_object_matcher ) ,
2023-08-25 10:13:29 +00:00
opt ( tuple ( ( space0 , tags ) ) ) ,
space0 ,
2023-04-19 23:53:37 +00:00
alt ( ( line_ending , eof ) ) ,
2023-03-24 20:37:34 +00:00
) ) ( input ) ? ;
2023-08-25 10:20:06 +00:00
Ok ( (
remaining ,
(
star_count ,
ws ,
2023-08-27 19:56:08 +00:00
maybe_todo_keyword ,
2023-08-25 10:20:06 +00:00
title ,
maybe_tags
. map ( | ( _ws , tags ) | {
tags . into_iter ( )
. map ( | single_tag | Into ::< & str > ::into ( single_tag ) )
. collect ( )
} )
. unwrap_or ( Vec ::new ( ) ) ,
) ,
) )
2023-03-23 23:35:32 +00:00
}
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 headline_title_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-08-25 10:13:29 +00:00
recognize ( tuple ( (
opt ( tuple ( ( space0 , tags , space0 ) ) ) ,
alt ( ( line_ending , eof ) ) ,
) ) ) ( input )
}
#[ cfg_attr(feature = " tracing " , tracing::instrument(ret, level = " debug " )) ]
fn tags < ' s > ( input : OrgSource < ' s > ) -> Res < OrgSource < ' s > , Vec < OrgSource < ' s > > > {
let ( remaining , ( _open , tags , _close ) ) =
tuple ( ( tag ( " : " ) , separated_list1 ( tag ( " : " ) , single_tag ) , tag ( " : " ) ) ) ( input ) ? ;
Ok ( ( remaining , tags ) )
}
#[ cfg_attr(feature = " tracing " , tracing::instrument(ret, level = " debug " )) ]
fn single_tag < ' r , ' s > ( input : OrgSource < ' s > ) -> Res < OrgSource < ' s > , OrgSource < ' s > > {
recognize ( many1 ( verify ( anychar , | c | {
c . is_alphanumeric ( ) | | " _@#% " . contains ( * c )
} ) ) ) ( input )
2023-03-23 23:53:20 +00:00
}
2023-04-25 02:10:24 +00:00
2023-08-27 19:56:08 +00:00
#[ cfg_attr(feature = " tracing " , tracing::instrument(ret, level = " debug " )) ]
fn heading_keyword < ' s > ( input : OrgSource < ' s > ) -> Res < OrgSource < ' s > , OrgSource < ' s > > {
// TODO: This should take into account the value of "#+TODO:" ref https://orgmode.org/manual/Per_002dfile-keywords.html and possibly the configurable variable org-todo-keywords ref https://orgmode.org/manual/Workflow-states.html. Case is significant.
alt ( ( tag ( " TODO " ) , tag ( " DONE " ) ) ) ( input )
}
2023-04-25 02:10:24 +00:00
impl < ' s > Document < ' s > {
pub fn iter_tokens < ' r > ( & ' r self ) -> impl Iterator < Item = Token < ' r , ' s > > {
2023-07-15 00:45:31 +00:00
AllTokensIterator ::new ( Token ::Document ( self ) )
2023-04-25 02:10:24 +00:00
}
}