Merge branch 'radio_target'
This commit is contained in:
commit
0ccee231e2
1
org_mode_samples/radio_link/before_and_after.org
Normal file
1
org_mode_samples/radio_link/before_and_after.org
Normal file
@ -0,0 +1 @@
|
|||||||
|
alpha *bar* baz foo <<<*bar* baz>>> lorem ipsum *bar* baz dolar.
|
1
org_mode_samples/radio_link/simple.org
Normal file
1
org_mode_samples/radio_link/simple.org
Normal file
@ -0,0 +1 @@
|
|||||||
|
foo <<<*bar* baz>>> lorem ipsum *bar* baz dolar.
|
@ -29,6 +29,8 @@ use crate::parser::PlainListItem;
|
|||||||
use crate::parser::PlainText;
|
use crate::parser::PlainText;
|
||||||
use crate::parser::Planning;
|
use crate::parser::Planning;
|
||||||
use crate::parser::PropertyDrawer;
|
use crate::parser::PropertyDrawer;
|
||||||
|
use crate::parser::RadioLink;
|
||||||
|
use crate::parser::RadioTarget;
|
||||||
use crate::parser::RegularLink;
|
use crate::parser::RegularLink;
|
||||||
use crate::parser::Section;
|
use crate::parser::Section;
|
||||||
use crate::parser::SrcBlock;
|
use crate::parser::SrcBlock;
|
||||||
@ -144,6 +146,8 @@ fn compare_object<'s>(
|
|||||||
Object::StrikeThrough(obj) => compare_strike_through(source, emacs, obj),
|
Object::StrikeThrough(obj) => compare_strike_through(source, emacs, obj),
|
||||||
Object::PlainText(obj) => compare_plain_text(source, emacs, obj),
|
Object::PlainText(obj) => compare_plain_text(source, emacs, obj),
|
||||||
Object::RegularLink(obj) => compare_regular_link(source, emacs, obj),
|
Object::RegularLink(obj) => compare_regular_link(source, emacs, obj),
|
||||||
|
Object::RadioLink(obj) => compare_radio_link(source, emacs, obj),
|
||||||
|
Object::RadioTarget(obj) => compare_radio_target(source, emacs, obj),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -915,16 +919,25 @@ fn compare_plain_text<'s>(
|
|||||||
let mut this_status = DiffStatus::Good;
|
let mut this_status = DiffStatus::Good;
|
||||||
let mut message = None;
|
let mut message = None;
|
||||||
let text = emacs.as_text()?;
|
let text = emacs.as_text()?;
|
||||||
let emacs_text_length = {
|
let start_ind: usize = text
|
||||||
let start_ind: usize = text.properties.get(0).expect("Should have start index.").as_atom()?.parse()?;
|
.properties
|
||||||
let end_ind: usize = text.properties.get(1).expect("Should have end index.").as_atom()?.parse()?;
|
.get(0)
|
||||||
end_ind - start_ind
|
.expect("Should have start index.")
|
||||||
};
|
.as_atom()?
|
||||||
|
.parse()?;
|
||||||
|
let end_ind: usize = text
|
||||||
|
.properties
|
||||||
|
.get(1)
|
||||||
|
.expect("Should have end index.")
|
||||||
|
.as_atom()?
|
||||||
|
.parse()?;
|
||||||
|
let emacs_text_length = end_ind - start_ind;
|
||||||
if rust.source.len() != emacs_text_length {
|
if rust.source.len() != emacs_text_length {
|
||||||
this_status = DiffStatus::Bad;
|
this_status = DiffStatus::Bad;
|
||||||
message = Some(format!(
|
message = Some(format!(
|
||||||
"(emacs len != rust len) {:?} != {:?}",
|
"(emacs len != rust len) {:?} != {:?}",
|
||||||
emacs_text_length, rust.source.len()
|
emacs_text_length,
|
||||||
|
rust.source.len()
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
let unquoted_text = text.unquote()?;
|
let unquoted_text = text.unquote()?;
|
||||||
@ -1104,3 +1117,49 @@ fn compare_regular_link<'s>(
|
|||||||
children: Vec::new(),
|
children: Vec::new(),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn compare_radio_link<'s>(
|
||||||
|
source: &'s str,
|
||||||
|
emacs: &'s Token<'s>,
|
||||||
|
rust: &'s RadioLink<'s>,
|
||||||
|
) -> Result<DiffResult, Box<dyn std::error::Error>> {
|
||||||
|
let mut this_status = DiffStatus::Good;
|
||||||
|
let emacs_name = "link";
|
||||||
|
if assert_name(emacs, emacs_name).is_err() {
|
||||||
|
this_status = DiffStatus::Bad;
|
||||||
|
}
|
||||||
|
|
||||||
|
if assert_bounds(source, emacs, rust).is_err() {
|
||||||
|
this_status = DiffStatus::Bad;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(DiffResult {
|
||||||
|
status: this_status,
|
||||||
|
name: emacs_name.to_owned(),
|
||||||
|
message: None,
|
||||||
|
children: Vec::new(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn compare_radio_target<'s>(
|
||||||
|
source: &'s str,
|
||||||
|
emacs: &'s Token<'s>,
|
||||||
|
rust: &'s RadioTarget<'s>,
|
||||||
|
) -> Result<DiffResult, Box<dyn std::error::Error>> {
|
||||||
|
let mut this_status = DiffStatus::Good;
|
||||||
|
let emacs_name = "radio-target";
|
||||||
|
if assert_name(emacs, emacs_name).is_err() {
|
||||||
|
this_status = DiffStatus::Bad;
|
||||||
|
}
|
||||||
|
|
||||||
|
if assert_bounds(source, emacs, rust).is_err() {
|
||||||
|
this_status = DiffStatus::Bad;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(DiffResult {
|
||||||
|
status: this_status,
|
||||||
|
name: emacs_name.to_owned(),
|
||||||
|
message: None,
|
||||||
|
children: Vec::new(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
@ -26,6 +26,7 @@ mod plain_list;
|
|||||||
mod plain_text;
|
mod plain_text;
|
||||||
mod planning;
|
mod planning;
|
||||||
mod property_drawer;
|
mod property_drawer;
|
||||||
|
mod radio_link;
|
||||||
mod regular_link;
|
mod regular_link;
|
||||||
pub mod sexp;
|
pub mod sexp;
|
||||||
mod source;
|
mod source;
|
||||||
@ -67,6 +68,8 @@ pub use object::Code;
|
|||||||
pub use object::Italic;
|
pub use object::Italic;
|
||||||
pub use object::Object;
|
pub use object::Object;
|
||||||
pub use object::PlainText;
|
pub use object::PlainText;
|
||||||
|
pub use object::RadioLink;
|
||||||
|
pub use object::RadioTarget;
|
||||||
pub use object::RegularLink;
|
pub use object::RegularLink;
|
||||||
pub use object::StrikeThrough;
|
pub use object::StrikeThrough;
|
||||||
pub use object::Underline;
|
pub use object::Underline;
|
||||||
|
@ -1,8 +1,10 @@
|
|||||||
use super::source::Source;
|
use super::source::Source;
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug, PartialEq)]
|
||||||
pub enum Object<'s> {
|
pub enum Object<'s> {
|
||||||
RegularLink(RegularLink<'s>),
|
RegularLink(RegularLink<'s>),
|
||||||
|
RadioLink(RadioLink<'s>),
|
||||||
|
RadioTarget(RadioTarget<'s>),
|
||||||
Bold(Bold<'s>),
|
Bold(Bold<'s>),
|
||||||
Italic(Italic<'s>),
|
Italic(Italic<'s>),
|
||||||
Underline(Underline<'s>),
|
Underline(Underline<'s>),
|
||||||
@ -12,52 +14,64 @@ pub enum Object<'s> {
|
|||||||
PlainText(PlainText<'s>),
|
PlainText(PlainText<'s>),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug, PartialEq)]
|
||||||
pub struct Bold<'s> {
|
pub struct Bold<'s> {
|
||||||
pub source: &'s str,
|
pub source: &'s str,
|
||||||
pub children: Vec<Object<'s>>,
|
pub children: Vec<Object<'s>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug, PartialEq)]
|
||||||
pub struct Italic<'s> {
|
pub struct Italic<'s> {
|
||||||
pub source: &'s str,
|
pub source: &'s str,
|
||||||
pub children: Vec<Object<'s>>,
|
pub children: Vec<Object<'s>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug, PartialEq)]
|
||||||
pub struct Underline<'s> {
|
pub struct Underline<'s> {
|
||||||
pub source: &'s str,
|
pub source: &'s str,
|
||||||
pub children: Vec<Object<'s>>,
|
pub children: Vec<Object<'s>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug, PartialEq)]
|
||||||
pub struct StrikeThrough<'s> {
|
pub struct StrikeThrough<'s> {
|
||||||
pub source: &'s str,
|
pub source: &'s str,
|
||||||
pub children: Vec<Object<'s>>,
|
pub children: Vec<Object<'s>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug, PartialEq)]
|
||||||
pub struct Code<'s> {
|
pub struct Code<'s> {
|
||||||
pub source: &'s str,
|
pub source: &'s str,
|
||||||
pub contents: &'s str,
|
pub contents: &'s str,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug, PartialEq)]
|
||||||
pub struct Verbatim<'s> {
|
pub struct Verbatim<'s> {
|
||||||
pub source: &'s str,
|
pub source: &'s str,
|
||||||
pub contents: &'s str,
|
pub contents: &'s str,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug, PartialEq)]
|
||||||
pub struct PlainText<'s> {
|
pub struct PlainText<'s> {
|
||||||
pub source: &'s str,
|
pub source: &'s str,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug, PartialEq)]
|
||||||
pub struct RegularLink<'s> {
|
pub struct RegularLink<'s> {
|
||||||
pub source: &'s str,
|
pub source: &'s str,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq)]
|
||||||
|
pub struct RadioTarget<'s> {
|
||||||
|
pub source: &'s str,
|
||||||
|
pub children: Vec<Object<'s>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq)]
|
||||||
|
pub struct RadioLink<'s> {
|
||||||
|
pub source: &'s str,
|
||||||
|
pub children: Vec<Object<'s>>,
|
||||||
|
}
|
||||||
|
|
||||||
impl<'s> Source<'s> for Object<'s> {
|
impl<'s> Source<'s> for Object<'s> {
|
||||||
fn get_source(&'s self) -> &'s str {
|
fn get_source(&'s self) -> &'s str {
|
||||||
match self {
|
match self {
|
||||||
@ -69,6 +83,8 @@ impl<'s> Source<'s> for Object<'s> {
|
|||||||
Object::Verbatim(obj) => obj.source,
|
Object::Verbatim(obj) => obj.source,
|
||||||
Object::PlainText(obj) => obj.source,
|
Object::PlainText(obj) => obj.source,
|
||||||
Object::RegularLink(obj) => obj.source,
|
Object::RegularLink(obj) => obj.source,
|
||||||
|
Object::RadioLink(obj) => obj.source,
|
||||||
|
Object::RadioTarget(obj) => obj.source,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -114,3 +130,15 @@ impl<'s> Source<'s> for RegularLink<'s> {
|
|||||||
self.source
|
self.source
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<'s> Source<'s> for RadioLink<'s> {
|
||||||
|
fn get_source(&'s self) -> &'s str {
|
||||||
|
self.source
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'s> Source<'s> for RadioTarget<'s> {
|
||||||
|
fn get_source(&'s self) -> &'s str {
|
||||||
|
self.source
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -8,6 +8,8 @@ use super::regular_link::regular_link;
|
|||||||
use super::Context;
|
use super::Context;
|
||||||
use crate::error::Res;
|
use crate::error::Res;
|
||||||
use crate::parser::object::Object;
|
use crate::parser::object::Object;
|
||||||
|
use crate::parser::radio_link::radio_link;
|
||||||
|
use crate::parser::radio_link::radio_target;
|
||||||
use crate::parser::text_markup::text_markup;
|
use crate::parser::text_markup::text_markup;
|
||||||
|
|
||||||
#[tracing::instrument(ret, level = "debug")]
|
#[tracing::instrument(ret, level = "debug")]
|
||||||
@ -19,6 +21,11 @@ pub fn standard_set_object<'r, 's>(
|
|||||||
not(|i| context.check_exit_matcher(i))(input)?;
|
not(|i| context.check_exit_matcher(i))(input)?;
|
||||||
|
|
||||||
alt((
|
alt((
|
||||||
|
map(parser_with_context!(radio_link)(context), Object::RadioLink),
|
||||||
|
map(
|
||||||
|
parser_with_context!(radio_target)(context),
|
||||||
|
Object::RadioTarget,
|
||||||
|
),
|
||||||
parser_with_context!(text_markup)(context),
|
parser_with_context!(text_markup)(context),
|
||||||
map(
|
map(
|
||||||
parser_with_context!(regular_link)(context),
|
parser_with_context!(regular_link)(context),
|
||||||
@ -33,7 +40,7 @@ pub fn minimal_set_object<'r, 's>(
|
|||||||
context: Context<'r, 's>,
|
context: Context<'r, 's>,
|
||||||
input: &'s str,
|
input: &'s str,
|
||||||
) -> Res<&'s str, Object<'s>> {
|
) -> Res<&'s str, Object<'s>> {
|
||||||
// TODO: add text markup, entities, LaTeX fragments, superscripts and subscripts
|
// TODO: add entities, LaTeX fragments, superscripts and subscripts
|
||||||
not(|i| context.check_exit_matcher(i))(input)?;
|
not(|i| context.check_exit_matcher(i))(input)?;
|
||||||
|
|
||||||
alt((
|
alt((
|
||||||
@ -49,6 +56,11 @@ pub fn any_object_except_plain_text<'r, 's>(
|
|||||||
) -> Res<&'s str, Object<'s>> {
|
) -> Res<&'s str, Object<'s>> {
|
||||||
// Used for exit matchers so this does not check exit matcher condition.
|
// Used for exit matchers so this does not check exit matcher condition.
|
||||||
alt((
|
alt((
|
||||||
|
map(parser_with_context!(radio_link)(context), Object::RadioLink),
|
||||||
|
map(
|
||||||
|
parser_with_context!(radio_target)(context),
|
||||||
|
Object::RadioTarget,
|
||||||
|
),
|
||||||
parser_with_context!(text_markup)(context),
|
parser_with_context!(text_markup)(context),
|
||||||
map(
|
map(
|
||||||
parser_with_context!(regular_link)(context),
|
parser_with_context!(regular_link)(context),
|
||||||
|
56
src/parser/radio_link.rs
Normal file
56
src/parser/radio_link.rs
Normal file
@ -0,0 +1,56 @@
|
|||||||
|
use nom::branch::alt;
|
||||||
|
use nom::bytes::complete::tag;
|
||||||
|
use nom::character::complete::line_ending;
|
||||||
|
use nom::character::complete::space0;
|
||||||
|
use nom::combinator::verify;
|
||||||
|
use nom::multi::many_till;
|
||||||
|
|
||||||
|
use super::Context;
|
||||||
|
use crate::error::Res;
|
||||||
|
use crate::parser::exiting::ExitClass;
|
||||||
|
use crate::parser::object_parser::minimal_set_object;
|
||||||
|
use crate::parser::parser_context::ContextElement;
|
||||||
|
use crate::parser::parser_context::ExitMatcherNode;
|
||||||
|
use crate::parser::parser_with_context::parser_with_context;
|
||||||
|
use crate::parser::util::exit_matcher_parser;
|
||||||
|
use crate::parser::util::get_consumed;
|
||||||
|
use crate::parser::util::not_yet_implemented;
|
||||||
|
use crate::parser::RadioLink;
|
||||||
|
use crate::parser::RadioTarget;
|
||||||
|
|
||||||
|
#[tracing::instrument(ret, level = "debug")]
|
||||||
|
pub fn radio_link<'r, 's>(context: Context<'r, 's>, input: &'s str) -> Res<&'s str, RadioLink<'s>> {
|
||||||
|
not_yet_implemented()?;
|
||||||
|
todo!();
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tracing::instrument(ret, level = "debug")]
|
||||||
|
pub fn radio_target<'r, 's>(
|
||||||
|
context: Context<'r, 's>,
|
||||||
|
input: &'s str,
|
||||||
|
) -> Res<&'s str, RadioTarget<'s>> {
|
||||||
|
let (remaining, _opening) = tag("<<<")(input)?;
|
||||||
|
let parser_context =
|
||||||
|
context.with_additional_node(ContextElement::ExitMatcherNode(ExitMatcherNode {
|
||||||
|
class: ExitClass::Beta,
|
||||||
|
exit_matcher: &radio_target_end,
|
||||||
|
}));
|
||||||
|
|
||||||
|
let (remaining, (children, _exit_contents)) = verify(
|
||||||
|
many_till(
|
||||||
|
parser_with_context!(minimal_set_object)(&parser_context),
|
||||||
|
parser_with_context!(exit_matcher_parser)(&parser_context),
|
||||||
|
),
|
||||||
|
|(children, _exit_contents)| !children.is_empty(),
|
||||||
|
)(remaining)?;
|
||||||
|
|
||||||
|
let (remaining, _closing) = tag(">>>")(remaining)?;
|
||||||
|
let (remaining, _trailing_whitespace) = space0(remaining)?;
|
||||||
|
let source = get_consumed(input, remaining);
|
||||||
|
Ok((remaining, RadioTarget { source, children }))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tracing::instrument(ret, level = "debug")]
|
||||||
|
fn radio_target_end<'r, 's>(context: Context<'r, 's>, input: &'s str) -> Res<&'s str, &'s str> {
|
||||||
|
alt((tag("<"), tag(">"), line_ending))(input)
|
||||||
|
}
|
@ -197,32 +197,6 @@ pub fn always_fail<'r, 's>(_context: Context<'r, 's>, input: &'s str) -> Res<&'s
|
|||||||
))))
|
))))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Walk backwards unconsuming blank lines and line endings.
|
|
||||||
///
|
|
||||||
/// List items are a special case where the trailing blank lines do not belong to it, unlike all other elements. Rather than write that special logic into each child parser, this just walks backwards through the consumed input to unconsume trailing blank lines and line breaks.
|
|
||||||
#[tracing::instrument(ret, level = "debug")]
|
|
||||||
pub fn regurgitate<'s>(input: &'s str, remaining: &'s str) -> &'s str {
|
|
||||||
assert!(is_slice_of(input, remaining));
|
|
||||||
let mut offset = remaining.as_ptr() as usize - input.as_ptr() as usize;
|
|
||||||
let source = &input[..offset];
|
|
||||||
let mut char_indices = source.char_indices().rev();
|
|
||||||
loop {
|
|
||||||
match char_indices.next() {
|
|
||||||
Some((off, chr)) => {
|
|
||||||
if chr == '\n' {
|
|
||||||
offset = off;
|
|
||||||
} else if chr != ' ' && chr != '\t' {
|
|
||||||
return &input[offset..];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
None => {
|
|
||||||
// It was all whitespace, so return the full input string
|
|
||||||
return input;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[tracing::instrument(ret, level = "debug")]
|
#[tracing::instrument(ret, level = "debug")]
|
||||||
pub fn whitespace_eof(input: &str) -> Res<&str, &str> {
|
pub fn whitespace_eof(input: &str) -> Res<&str, &str> {
|
||||||
recognize(tuple((multispace0, eof)))(input)
|
recognize(tuple((multispace0, eof)))(input)
|
||||||
@ -236,6 +210,13 @@ pub fn text_until_exit<'r, 's>(context: Context<'r, 's>, input: &'s str) -> Res<
|
|||||||
))(input)
|
))(input)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[allow(dead_code)]
|
||||||
|
pub fn not_yet_implemented() -> Res<&'static str, ()> {
|
||||||
|
return Err(nom::Err::Error(CustomError::MyError(MyError(
|
||||||
|
"Not implemented yet.",
|
||||||
|
))));
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
@ -249,14 +230,4 @@ mod tests {
|
|||||||
assert!(is_slice_of(input, yellow_heart));
|
assert!(is_slice_of(input, yellow_heart));
|
||||||
assert_eq!(yellow_heart, "💛");
|
assert_eq!(yellow_heart, "💛");
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn regurgitate_unicode() {
|
|
||||||
let input = "🧡💛\n\t \t \n\n💚💙💜";
|
|
||||||
let (green_heart_index, _) = input.char_indices().skip(12).next().unwrap();
|
|
||||||
let starting_with_green_heart = &input[green_heart_index..];
|
|
||||||
let after_yellow = regurgitate(input, starting_with_green_heart);
|
|
||||||
assert!(is_slice_of(input, after_yellow));
|
|
||||||
assert_eq!(after_yellow, "\n\t \t \n\n💚💙💜");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user