10 Commits

Author SHA1 Message Date
Tom Alexander
10aa0956ee Merge branch 'lesser_block_memory_optimization'
All checks were successful
clippy Build clippy has succeeded
rustfmt Build rustfmt has succeeded
rust-foreign-document-test Build rust-foreign-document-test has succeeded
rust-build Build rust-build has succeeded
rust-test Build rust-test has succeeded
2023-10-27 22:13:25 -04:00
Tom Alexander
816c164996 Only allocate memory if removing text for lesser blocks.
All checks were successful
clippy Build clippy has succeeded
rust-build Build rust-build has succeeded
rust-foreign-document-test Build rust-foreign-document-test has succeeded
rust-test Build rust-test has succeeded
2023-10-27 21:50:08 -04:00
Tom Alexander
ee201e1336 Merge branch 'explicit_all_node_iter'
All checks were successful
rustfmt Build rustfmt has succeeded
clippy Build clippy has succeeded
rust-foreign-document-test Build rust-foreign-document-test has succeeded
rust-build Build rust-build has succeeded
rust-test Build rust-test has succeeded
2023-10-23 18:39:16 -04:00
Tom Alexander
4897952330 Make creating AllAstNodeIter explicit.
All checks were successful
clippy Build clippy has succeeded
rust-foreign-document-test Build rust-foreign-document-test has succeeded
rust-build Build rust-build has succeeded
rust-test Build rust-test has succeeded
This is to remove the ambiguity between calling iter on the specific structs like Document and calling iter on an AstNode by having an explicitly-named function to create the iterator.
2023-10-23 18:25:59 -04:00
Tom Alexander
e1d85c6dc2 Merge branch 'remove_set_source'
All checks were successful
rustfmt Build rustfmt has succeeded
clippy Build clippy has succeeded
rust-build Build rust-build has succeeded
rust-foreign-document-test Build rust-foreign-document-test has succeeded
rust-test Build rust-test has succeeded
2023-10-23 18:06:56 -04:00
Tom Alexander
c420ccd029 Fix clippy errors.
All checks were successful
clippy Build clippy has succeeded
rust-foreign-document-test Build rust-foreign-document-test has succeeded
rust-build Build rust-build has succeeded
rust-test Build rust-test has succeeded
2023-10-23 17:43:43 -04:00
Tom Alexander
a880629831 Make clippy not write to the host git repo. 2023-10-23 17:43:32 -04:00
Tom Alexander
5e2dea1f28 Remove the SetSource trait.
It was only being used for creating paragraphs of specific text, so I just adjusted the of_text function to handle it.
2023-10-23 17:43:32 -04:00
Tom Alexander
f47d688be4 Remove owned String from CustomError.
Some checks failed
rustfmt Build rustfmt has failed
rust-test Build rust-test has failed
clippy Build clippy has failed
rust-build Build rust-build has failed
rust-foreign-document-test Build rust-foreign-document-test has succeeded
This is a 15% performance improvement.
2023-10-21 14:29:37 -04:00
Tom Alexander
acfc5e5e68 Only allocate memory when unquoting sexp string that contains escapes.
All checks were successful
rust-build Build rust-build has succeeded
rust-foreign-document-test Build rust-foreign-document-test has succeeded
rust-test Build rust-test has succeeded
clippy Build clippy has succeeded
rustfmt Build rustfmt has succeeded
If the quoted string contains no escape sequences, then unquoting the string can be done by simply shaving off the leading and trailing quotation marks which can be a slice operation. By returning Cow, we can return either a borrowed slice or an owned String.
2023-10-20 12:53:27 -04:00
21 changed files with 206 additions and 146 deletions

View File

@@ -45,10 +45,6 @@ dockerclippy:
clippy: clippy:
> cargo clippy --no-deps --all-targets --all-features -- -D warnings > cargo clippy --no-deps --all-targets --all-features -- -D warnings
.PHONY: clippyfix
clippyfix:
> cargo clippy --fix --lib -p organic --all-features
.PHONY: test .PHONY: test
test: test:
> cargo test --no-default-features --features compare --no-fail-fast --lib --test test_loader -- --test-threads $(TESTJOBS) > cargo test --no-default-features --features compare --no-fail-fast --lib --test test_loader -- --test-threads $(TESTJOBS)

View File

@@ -25,13 +25,13 @@ ifdef REMOTE_REPO
else else
@echo "REMOTE_REPO not defined, not removing from remote repo." @echo "REMOTE_REPO not defined, not removing from remote repo."
endif endif
docker volume rm cargo-cache docker volume rm rust-cache cargo-cache
# NOTE: This target will write to folders underneath the git-root # NOTE: This target will write to folders underneath the git-root
.PHONY: run .PHONY: run
run: build run: build
docker run --rm --init --read-only --mount type=tmpfs,destination=/tmp -v "$$(readlink -f ../../):/source" --workdir=/source --mount source=cargo-cache,target=/usr/local/cargo/registry $(IMAGE_NAME) docker run --rm --init --read-only --mount type=tmpfs,destination=/tmp -v "$$(readlink -f ../../):/source:ro" --workdir=/source --mount source=cargo-cache,target=/usr/local/cargo/registry --mount source=rust-cache,target=/target --env CARGO_TARGET_DIR=/target $(IMAGE_NAME)
.PHONY: shell .PHONY: shell
shell: build shell: build
docker run --rm -i -t --entrypoint /bin/sh --mount type=tmpfs,destination=/tmp -v "$$(readlink -f ../../):/source" --workdir=/source --mount source=cargo-cache,target=/usr/local/cargo/registry $(IMAGE_NAME) docker run --rm -i -t --entrypoint /bin/sh --mount type=tmpfs,destination=/tmp -v "$$(readlink -f ../../):/source:ro" --workdir=/source --mount source=cargo-cache,target=/usr/local/cargo/registry --mount source=rust-cache,target=/target --env CARGO_TARGET_DIR=/target $(IMAGE_NAME)

View File

@@ -1,3 +0,0 @@
foo <<bar>> baz
lorem << ipsum >> dolar

View File

@@ -1,3 +1,5 @@
use std::borrow::Borrow;
use std::borrow::Cow;
use std::collections::BTreeSet; use std::collections::BTreeSet;
use std::fmt::Debug; use std::fmt::Debug;
use std::str::FromStr; use std::str::FromStr;
@@ -262,11 +264,11 @@ pub(crate) fn compare_property_set_of_quoted_string<
.iter() .iter()
.map(|e| e.as_atom()) .map(|e| e.as_atom())
.collect::<Result<Vec<_>, _>>()?; .collect::<Result<Vec<_>, _>>()?;
let value: Vec<String> = value let value: Vec<Cow<'_, str>> = value
.into_iter() .into_iter()
.map(unquote) .map(unquote)
.collect::<Result<Vec<_>, _>>()?; .collect::<Result<Vec<_>, _>>()?;
let value: BTreeSet<&str> = value.iter().map(|e| e.as_str()).collect(); let value: BTreeSet<&str> = value.iter().map(|e| e.borrow()).collect();
let mismatched: Vec<_> = value.symmetric_difference(&rust_value).copied().collect(); let mismatched: Vec<_> = value.symmetric_difference(&rust_value).copied().collect();
if !mismatched.is_empty() { if !mismatched.is_empty() {
let this_status = DiffStatus::Bad; let this_status = DiffStatus::Bad;
@@ -653,7 +655,7 @@ pub(crate) fn compare_property_number_lines<
(Some(number_lines), Some(rust_number_lines)) => { (Some(number_lines), Some(rust_number_lines)) => {
let token_list = number_lines.as_list()?; let token_list = number_lines.as_list()?;
let number_type = token_list let number_type = token_list
.get(0) .first()
.map(Token::as_atom) .map(Token::as_atom)
.map_or(Ok(None), |r| r.map(Some))? .map_or(Ok(None), |r| r.map(Some))?
.ok_or(":number-lines should have a type.")?; .ok_or(":number-lines should have a type.")?;

View File

@@ -1576,7 +1576,7 @@ fn compare_example_block<'b, 's>(
[], [],
( (
EmacsField::Required(":value"), EmacsField::Required(":value"),
|r| Some(r.contents.as_str()), |r| Some(&r.contents),
compare_property_quoted_string compare_property_quoted_string
), ),
( (
@@ -1654,7 +1654,7 @@ fn compare_export_block<'b, 's>(
), ),
( (
EmacsField::Required(":value"), EmacsField::Required(":value"),
|r| Some(r.contents.as_str()), |r| Some(&r.contents),
compare_property_quoted_string compare_property_quoted_string
) )
) { ) {
@@ -1702,7 +1702,7 @@ fn compare_src_block<'b, 's>(
), ),
( (
EmacsField::Required(":value"), EmacsField::Required(":value"),
|r| Some(r.contents.as_str()), |r| Some(&r.contents),
compare_property_quoted_string compare_property_quoted_string
), ),
( (
@@ -2153,7 +2153,7 @@ fn compare_plain_text<'b, 's>(
let text = emacs.as_text()?; let text = emacs.as_text()?;
let start_ind: usize = text let start_ind: usize = text
.properties .properties
.get(0) .first()
.expect("Should have start index.") .expect("Should have start index.")
.as_atom()? .as_atom()?
.parse()?; .parse()?;

View File

@@ -1,3 +1,4 @@
use std::borrow::Cow;
use std::collections::HashMap; use std::collections::HashMap;
use nom::branch::alt; use nom::branch::alt;
@@ -36,12 +37,6 @@ pub struct TextWithProperties<'s> {
pub(crate) properties: Vec<Token<'s>>, pub(crate) properties: Vec<Token<'s>>,
} }
enum ParseState {
Normal,
Escape,
Octal(Vec<u8>),
}
impl<'s> Token<'s> { impl<'s> Token<'s> {
pub(crate) fn as_vector<'p>( pub(crate) fn as_vector<'p>(
&'p self, &'p self,
@@ -117,8 +112,27 @@ fn get_consumed<'s>(input: &'s str, remaining: &'s str) -> &'s str {
&input[..offset] &input[..offset]
} }
pub(crate) fn unquote(text: &str) -> Result<String, Box<dyn std::error::Error>> { #[derive(Debug)]
let mut out: Vec<u8> = Vec::with_capacity(text.len()); enum UnquoteState {
Normal,
Escape,
HasEscape {
out: Vec<u8>,
},
HasEscapeEscape {
out: Vec<u8>,
},
Octal {
octal_begin_offset: usize,
octal: Vec<u8>,
},
HasEscapeOctal {
out: Vec<u8>,
octal: Vec<u8>,
},
}
pub(crate) fn unquote(text: &str) -> Result<Cow<'_, str>, Box<dyn std::error::Error>> {
if !text.starts_with('"') { if !text.starts_with('"') {
return Err("Quoted text does not start with quote.".into()); return Err("Quoted text does not start with quote.".into());
} }
@@ -126,54 +140,143 @@ pub(crate) fn unquote(text: &str) -> Result<String, Box<dyn std::error::Error>>
return Err("Quoted text does not end with quote.".into()); return Err("Quoted text does not end with quote.".into());
} }
let interior_text = &text[1..(text.len() - 1)]; let interior_text = &text[1..(text.len() - 1)];
let mut state = ParseState::Normal; let mut state = UnquoteState::Normal;
for current_char in interior_text.bytes() { for (offset, current_char) in interior_text.bytes().enumerate() {
// Check to see if octal finished // Check to see if octal finished
state = match (state, current_char) { state = match (state, current_char) {
(ParseState::Octal(octal), b'0'..=b'7') if octal.len() < MAX_OCTAL_LENGTH => { (
ParseState::Octal(octal) UnquoteState::Octal {
octal_begin_offset,
octal,
},
b'0'..=b'7',
) if octal.len() < MAX_OCTAL_LENGTH => UnquoteState::Octal {
octal_begin_offset,
octal,
},
(
UnquoteState::Octal {
octal_begin_offset,
octal,
},
_,
) => {
let octal_number_string = String::from_utf8(octal)?;
let decoded_byte = u8::from_str_radix(&octal_number_string, 8)?;
let mut out: Vec<u8> = Vec::with_capacity(interior_text.len());
out.extend_from_slice(&interior_text.as_bytes()[..octal_begin_offset]);
out.push(decoded_byte);
UnquoteState::HasEscape { out }
} }
(ParseState::Octal(octal), _) => { (UnquoteState::HasEscapeOctal { out, octal }, b'0'..=b'7')
if octal.len() < MAX_OCTAL_LENGTH =>
{
UnquoteState::HasEscapeOctal { out, octal }
}
(UnquoteState::HasEscapeOctal { mut out, octal }, _) => {
let octal_number_string = String::from_utf8(octal)?; let octal_number_string = String::from_utf8(octal)?;
let decoded_byte = u8::from_str_radix(&octal_number_string, 8)?; let decoded_byte = u8::from_str_radix(&octal_number_string, 8)?;
out.push(decoded_byte); out.push(decoded_byte);
ParseState::Normal UnquoteState::HasEscape { out }
} }
(state, _) => state, (state, _) => state,
}; };
state = match (state, current_char) { state = match (state, current_char) {
(ParseState::Normal, b'\\') => ParseState::Escape, (UnquoteState::Normal, b'\\') => UnquoteState::Escape,
(ParseState::Normal, _) => { (UnquoteState::Normal, _) => UnquoteState::Normal,
(UnquoteState::HasEscape { out }, b'\\') => UnquoteState::HasEscapeEscape { out },
(UnquoteState::HasEscape { mut out }, _) => {
out.push(current_char); out.push(current_char);
ParseState::Normal UnquoteState::HasEscape { out }
} }
(ParseState::Escape, b'n') => { (UnquoteState::Escape, b'n') => {
let mut out: Vec<u8> = Vec::with_capacity(interior_text.len());
// Subtract 1 from offset to account for backslash.
out.extend_from_slice(&interior_text.as_bytes()[..(offset - 1)]);
out.push(b'\n'); out.push(b'\n');
ParseState::Normal UnquoteState::HasEscape { out }
} }
(ParseState::Escape, b'\\') => { (UnquoteState::HasEscapeEscape { mut out }, b'n') => {
out.push(b'\n');
UnquoteState::HasEscape { out }
}
(UnquoteState::Escape, b'\\') => {
let mut out: Vec<u8> = Vec::with_capacity(interior_text.len());
// Subtract 1 from offset to account for backslash.
out.extend_from_slice(&interior_text.as_bytes()[..(offset - 1)]);
out.push(b'\\'); out.push(b'\\');
ParseState::Normal UnquoteState::HasEscape { out }
} }
(ParseState::Escape, b'"') => { (UnquoteState::HasEscapeEscape { mut out }, b'\\') => {
out.push(b'\\');
UnquoteState::HasEscape { out }
}
(UnquoteState::Escape, b'"') => {
let mut out: Vec<u8> = Vec::with_capacity(interior_text.len());
// Subtract 1 from offset to account for backslash.
out.extend_from_slice(&interior_text.as_bytes()[..(offset - 1)]);
out.push(b'"'); out.push(b'"');
ParseState::Normal UnquoteState::HasEscape { out }
} }
(ParseState::Escape, b'0'..=b'7') => { (UnquoteState::HasEscapeEscape { mut out }, b'"') => {
out.push(b'"');
UnquoteState::HasEscape { out }
}
(UnquoteState::Escape, b'0'..=b'7') => {
let mut octal = Vec::with_capacity(MAX_OCTAL_LENGTH); let mut octal = Vec::with_capacity(MAX_OCTAL_LENGTH);
octal.push(current_char); octal.push(current_char);
ParseState::Octal(octal) // Substract 1 from offset to account for backslash
UnquoteState::Octal {
octal_begin_offset: offset - 1,
octal,
}
} }
(ParseState::Octal(mut octal), b'0'..=b'7') => { (UnquoteState::HasEscapeEscape { out }, b'0'..=b'7') => {
let mut octal = Vec::with_capacity(MAX_OCTAL_LENGTH);
octal.push(current_char); octal.push(current_char);
ParseState::Octal(octal) // Substract 1 from offset to account for backslash
UnquoteState::HasEscapeOctal { out, octal }
} }
_ => panic!("Invalid state unquoting string."), (
UnquoteState::Octal {
octal_begin_offset,
mut octal,
},
b'0'..=b'7',
) => {
octal.push(current_char);
UnquoteState::Octal {
octal_begin_offset,
octal,
}
}
(UnquoteState::HasEscapeOctal { out, mut octal }, b'0'..=b'7') => {
octal.push(current_char);
UnquoteState::HasEscapeOctal { out, octal }
}
(state, _) => panic!(
"Invalid state unquoting string: {:?} | {} | {:?}",
state, offset, interior_text
),
}; };
} }
Ok(String::from_utf8(out)?) match state {
UnquoteState::Normal | UnquoteState::Escape | UnquoteState::Octal { .. } => {
Ok(Cow::Borrowed(interior_text))
}
UnquoteState::HasEscape { out } => Ok(Cow::Owned(String::from_utf8(out)?)),
UnquoteState::HasEscapeEscape { mut out } => {
out.push(b'\\');
Ok(Cow::Owned(String::from_utf8(out)?))
}
UnquoteState::HasEscapeOctal { mut out, octal } => {
out.push(b'\\');
out.extend(octal);
Ok(Cow::Owned(String::from_utf8(out)?))
}
}
} }
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] #[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]

View File

@@ -1,3 +1,4 @@
use std::borrow::Cow;
use std::str::FromStr; use std::str::FromStr;
use super::compare_field::compare_property_list_of_quoted_string; use super::compare_field::compare_property_list_of_quoted_string;
@@ -206,10 +207,10 @@ pub(crate) fn get_property_unquoted_atom<'s>(
/// Get a named property containing an quoted string from the emacs token. /// Get a named property containing an quoted string from the emacs token.
/// ///
/// Returns None if key is not found. /// Returns None if key is not found.
pub(crate) fn get_property_quoted_string( pub(crate) fn get_property_quoted_string<'s>(
emacs: &Token<'_>, emacs: &Token<'s>,
key: &str, key: &str,
) -> Result<Option<String>, Box<dyn std::error::Error>> { ) -> Result<Option<Cow<'s, str>>, Box<dyn std::error::Error>> {
get_property(emacs, key)? get_property(emacs, key)?
.map(Token::as_atom) .map(Token::as_atom)
.map_or(Ok(None), |r| r.map(Some))? .map_or(Ok(None), |r| r.map(Some))?

View File

@@ -6,8 +6,6 @@ pub(crate) type Res<T, U> = IResult<T, U, CustomError>;
#[derive(Debug)] #[derive(Debug)]
pub enum CustomError { pub enum CustomError {
#[allow(dead_code)]
Text(String),
Static(&'static str), Static(&'static str),
IO(std::io::Error), IO(std::io::Error),
Parser(ErrorKind), Parser(ErrorKind),
@@ -35,9 +33,3 @@ impl From<&'static str> for CustomError {
CustomError::Static(value) CustomError::Static(value)
} }
} }
impl From<String> for CustomError {
fn from(value: String) -> Self {
CustomError::Text(value)
}
}

View File

@@ -90,12 +90,11 @@ impl<'r, 's> Iterator for AllAstNodeIter<'r, 's> {
} }
} }
impl<'r, 's> IntoIterator for AstNode<'r, 's> { impl<'r, 's> AstNode<'r, 's> {
type Item = AstNode<'r, 's>; /// Iterate all AST nodes.
///
type IntoIter = AllAstNodeIter<'r, 's>; /// This is different from the iter/into_iter functions which iterate a single level of the children. This iterates the entire tree including returning the root node itself.
pub fn iter_all_ast_nodes(self) -> AllAstNodeIter<'r, 's> {
fn into_iter(self) -> Self::IntoIter {
AllAstNodeIter { AllAstNodeIter {
root: Some(self), root: Some(self),
queue: VecDeque::new(), queue: VecDeque::new(),

View File

@@ -15,10 +15,8 @@ use crate::context::RefContext;
use crate::error::CustomError; use crate::error::CustomError;
use crate::error::Res; use crate::error::Res;
use crate::parser::macros::element; use crate::parser::macros::element;
use crate::types::AffiliatedKeywords;
use crate::types::Object; use crate::types::Object;
use crate::types::Paragraph; use crate::types::Paragraph;
use crate::types::PlainText;
#[cfg_attr( #[cfg_attr(
feature = "tracing", feature = "tracing",
@@ -80,13 +78,10 @@ pub(crate) fn broken_end<'b, 'g, 'r, 's>(
Ok(( Ok((
remaining, remaining,
Paragraph { Paragraph::of_text(
source: input.get_until(remaining).into(), input.get_until(remaining).into(),
affiliated_keywords: AffiliatedKeywords::default(), input.get_until(lead_in_remaining).into(),
children: vec![Object::PlainText(PlainText { ),
source: input.get_until(lead_in_remaining).into(),
})],
},
)) ))
} }
} }
@@ -139,13 +134,10 @@ pub(crate) fn broken_dynamic_block<'b, 'g, 'r, 's>(
Ok(( Ok((
remaining, remaining,
Paragraph { Paragraph::of_text(
source: input.get_until(remaining).into(), input.get_until(remaining).into(),
affiliated_keywords: AffiliatedKeywords::default(), input.get_until(lead_in_remaining).into(),
children: vec![Object::PlainText(PlainText { ),
source: input.get_until(lead_in_remaining).into(),
})],
},
)) ))
} }
} }

View File

@@ -235,7 +235,7 @@ mod tests {
assert_eq!( assert_eq!(
first_paragraph first_paragraph
.children .children
.get(0) .first()
.expect("Len already asserted to be 1"), .expect("Len already asserted to be 1"),
&Object::Citation(Citation { &Object::Citation(Citation {
source: "[cite:@foo]", source: "[cite:@foo]",

View File

@@ -143,7 +143,7 @@ fn document_org_source<'b, 'g, 'r, 's>(
{ {
// If there are radio targets in this document then we need to parse the entire document again with the knowledge of the radio targets. // 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<'_>>> = Into::<AstNode>::into(&document) let all_radio_targets: Vec<&Vec<Object<'_>>> = Into::<AstNode>::into(&document)
.into_iter() .iter_all_ast_nodes()
.filter_map(|ast_node| { .filter_map(|ast_node| {
if let AstNode::RadioTarget(ast_node) = ast_node { if let AstNode::RadioTarget(ast_node) = ast_node {
Some(ast_node) Some(ast_node)

View File

@@ -31,7 +31,6 @@ use crate::types::Drawer;
use crate::types::Element; use crate::types::Element;
use crate::types::Keyword; use crate::types::Keyword;
use crate::types::Paragraph; use crate::types::Paragraph;
use crate::types::SetSource;
#[cfg_attr( #[cfg_attr(
feature = "tracing", feature = "tracing",
@@ -81,9 +80,8 @@ where
))(remaining) ))(remaining)
{ {
Ok((remain, (_not_immediate_exit, first_line, (_trailing_whitespace, _exit_contents)))) => { Ok((remain, (_not_immediate_exit, first_line, (_trailing_whitespace, _exit_contents)))) => {
let mut element = Element::Paragraph(Paragraph::of_text(first_line.into()));
let source = get_consumed(remaining, remain); let source = get_consumed(remaining, remain);
element.set_source(source.into()); let element = Element::Paragraph(Paragraph::of_text(source.into(), first_line.into()));
(remain, vec![element]) (remain, vec![element])
} }
Err(_) => { Err(_) => {

View File

@@ -37,7 +37,6 @@ use crate::types::DynamicBlock;
use crate::types::Element; use crate::types::Element;
use crate::types::Keyword; use crate::types::Keyword;
use crate::types::Paragraph; use crate::types::Paragraph;
use crate::types::SetSource;
#[cfg_attr( #[cfg_attr(
feature = "tracing", feature = "tracing",
@@ -88,9 +87,7 @@ where
))))(remaining)?; ))))(remaining)?;
let leading_blank_lines = let leading_blank_lines =
leading_blank_lines.map(|(source, (first_line, _remaining_lines))| { leading_blank_lines.map(|(source, (first_line, _remaining_lines))| {
let mut element = Element::Paragraph(Paragraph::of_text(first_line.into())); Element::Paragraph(Paragraph::of_text(source.into(), first_line.into()))
element.set_source(source.into());
element
}); });
let (remaining, (mut children, _exit_contents)) = let (remaining, (mut children, _exit_contents)) =
many_till(element_matcher, exit_matcher)(remaining)?; many_till(element_matcher, exit_matcher)(remaining)?;

View File

@@ -39,7 +39,6 @@ use crate::types::Element;
use crate::types::Keyword; use crate::types::Keyword;
use crate::types::Paragraph; use crate::types::Paragraph;
use crate::types::QuoteBlock; use crate::types::QuoteBlock;
use crate::types::SetSource;
use crate::types::SpecialBlock; use crate::types::SpecialBlock;
#[cfg_attr( #[cfg_attr(
@@ -257,9 +256,7 @@ fn greater_block_body<'c, 'b, 'g, 'r, 's>(
))))(remaining)?; ))))(remaining)?;
let leading_blank_lines = let leading_blank_lines =
leading_blank_lines.map(|(source, (first_line, _remaining_lines))| { leading_blank_lines.map(|(source, (first_line, _remaining_lines))| {
let mut element = Element::Paragraph(Paragraph::of_text(first_line.into())); Element::Paragraph(Paragraph::of_text(source.into(), first_line.into()))
element.set_source(source.into());
element
}); });
let (remaining, (mut children, _exit_contents)) = let (remaining, (mut children, _exit_contents)) =
many_till(element_matcher, exit_matcher)(remaining)?; many_till(element_matcher, exit_matcher)(remaining)?;

View File

@@ -102,7 +102,7 @@ pub(crate) fn apply_in_buffer_settings<'g, 's, 'sf>(
}) { }) {
let (_, (in_progress_words, complete_words)) = let (_, (in_progress_words, complete_words)) =
todo_keywords(kw.value).map_err(|err| match err { todo_keywords(kw.value).map_err(|err| match err {
nom::Err::Incomplete(_) => CustomError::Text(err.to_string()), nom::Err::Incomplete(_) => panic!("This parser does not support streaming."),
nom::Err::Error(e) => e, nom::Err::Error(e) => e,
nom::Err::Failure(e) => e, nom::Err::Failure(e) => e,
})?; })?;
@@ -123,7 +123,7 @@ pub(crate) fn apply_in_buffer_settings<'g, 's, 'sf>(
kw.value, kw.value,
) )
.map_err(|err: nom::Err<_>| match err { .map_err(|err: nom::Err<_>| match err {
nom::Err::Incomplete(_) => CustomError::Text(err.to_string()), nom::Err::Incomplete(_) => panic!("This parser does not support streaming."),
nom::Err::Error(e) => e, nom::Err::Error(e) => e,
nom::Err::Failure(e) => e, nom::Err::Failure(e) => e,
})?; })?;
@@ -141,7 +141,7 @@ pub(crate) fn apply_in_buffer_settings<'g, 's, 'sf>(
.filter(|kw| kw.key.eq_ignore_ascii_case("link")) .filter(|kw| kw.key.eq_ignore_ascii_case("link"))
{ {
let (_, (link_key, link_value)) = link_template(kw.value).map_err(|err| match err { let (_, (link_key, link_value)) = link_template(kw.value).map_err(|err| match err {
nom::Err::Incomplete(_) => CustomError::Text(err.to_string()), nom::Err::Incomplete(_) => panic!("This parser does not support streaming."),
nom::Err::Error(e) => e, nom::Err::Error(e) => e,
nom::Err::Failure(e) => e, nom::Err::Failure(e) => e,
})?; })?;
@@ -157,7 +157,7 @@ pub(crate) fn apply_in_buffer_settings<'g, 's, 'sf>(
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] #[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
pub(crate) fn apply_post_parse_in_buffer_settings<'g, 's, 'sf>(document: &mut Document<'s>) { pub(crate) fn apply_post_parse_in_buffer_settings<'g, 's, 'sf>(document: &mut Document<'s>) {
document.category = Into::<AstNode>::into(&*document) document.category = Into::<AstNode>::into(&*document)
.into_iter() .iter_all_ast_nodes()
.filter_map(|ast_node| { .filter_map(|ast_node| {
if let AstNode::Keyword(ast_node) = ast_node { if let AstNode::Keyword(ast_node) = ast_node {
if ast_node.key.eq_ignore_ascii_case("category") { if ast_node.key.eq_ignore_ascii_case("category") {

View File

@@ -1,3 +1,5 @@
use std::borrow::Cow;
use nom::branch::alt; use nom::branch::alt;
use nom::bytes::complete::is_not; use nom::bytes::complete::is_not;
use nom::bytes::complete::tag; use nom::bytes::complete::tag;
@@ -651,6 +653,11 @@ fn switch_word<'s>(input: OrgSource<'s>) -> Res<OrgSource<'s>, OrgSource<'s>> {
))(input) ))(input)
} }
enum ContentState {
Normal,
Modified(String),
}
#[cfg_attr( #[cfg_attr(
feature = "tracing", feature = "tracing",
tracing::instrument(ret, level = "debug", skip(context)) tracing::instrument(ret, level = "debug", skip(context))
@@ -658,8 +665,8 @@ fn switch_word<'s>(input: OrgSource<'s>) -> Res<OrgSource<'s>, OrgSource<'s>> {
pub(crate) fn content<'b, 'g, 'r, 's>( pub(crate) fn content<'b, 'g, 'r, 's>(
context: RefContext<'b, 'g, 'r, 's>, context: RefContext<'b, 'g, 'r, 's>,
input: OrgSource<'s>, input: OrgSource<'s>,
) -> Res<OrgSource<'s>, String> { ) -> Res<OrgSource<'s>, Cow<'s, str>> {
let mut ret = String::new(); let mut state = ContentState::Normal;
let mut remaining = input; let mut remaining = input;
let exit_matcher_parser = parser_with_context!(exit_matcher_parser)(context); let exit_matcher_parser = parser_with_context!(exit_matcher_parser)(context);
loop { loop {
@@ -669,13 +676,28 @@ pub(crate) fn content<'b, 'g, 'r, 's>(
let (remain, (pre_escape_whitespace, line)) = content_line(remaining)?; let (remain, (pre_escape_whitespace, line)) = content_line(remaining)?;
if let Some(val) = pre_escape_whitespace { if let Some(val) = pre_escape_whitespace {
ret.push_str(Into::<&str>::into(val)); if let ContentState::Modified(ref mut ret) = state {
ret.push_str(Into::<&str>::into(val));
} else {
let mut ret = String::new();
ret.push_str(Into::<&str>::into(input.get_until(remaining)));
ret.push_str(Into::<&str>::into(val));
state = ContentState::Modified(ret);
}
}
if let ContentState::Modified(ref mut ret) = state {
ret.push_str(line.into());
} }
ret.push_str(line.into());
remaining = remain; remaining = remain;
} }
Ok((remaining, ret)) match state {
ContentState::Normal => Ok((
remaining,
Cow::Borrowed(Into::<&str>::into(input.get_until(remaining))),
)),
ContentState::Modified(ret) => Ok((remaining, Cow::Owned(ret))),
}
} }
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] #[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]

View File

@@ -22,7 +22,6 @@ use super::CenterBlock;
use super::Drawer; use super::Drawer;
use super::GetStandardProperties; use super::GetStandardProperties;
use super::QuoteBlock; use super::QuoteBlock;
use super::SetSource;
use super::SpecialBlock; use super::SpecialBlock;
use super::StandardProperties; use super::StandardProperties;
@@ -55,38 +54,6 @@ pub enum Element<'s> {
LatexEnvironment(LatexEnvironment<'s>), LatexEnvironment(LatexEnvironment<'s>),
} }
impl<'s> SetSource<'s> for Element<'s> {
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
fn set_source(&mut self, source: &'s str) {
match self {
Element::Paragraph(obj) => obj.source = source,
Element::PlainList(obj) => obj.source = source,
Element::CenterBlock(obj) => obj.source = source,
Element::QuoteBlock(obj) => obj.source = source,
Element::SpecialBlock(obj) => obj.source = source,
Element::DynamicBlock(obj) => obj.source = source,
Element::FootnoteDefinition(obj) => obj.source = source,
Element::Comment(obj) => obj.source = source,
Element::Drawer(obj) => obj.source = source,
Element::PropertyDrawer(obj) => obj.source = source,
Element::Table(obj) => obj.source = source,
Element::VerseBlock(obj) => obj.source = source,
Element::CommentBlock(obj) => obj.source = source,
Element::ExampleBlock(obj) => obj.source = source,
Element::ExportBlock(obj) => obj.source = source,
Element::SrcBlock(obj) => obj.source = source,
Element::Clock(obj) => obj.source = source,
Element::DiarySexp(obj) => obj.source = source,
Element::Planning(obj) => obj.source = source,
Element::FixedWidthArea(obj) => obj.source = source,
Element::HorizontalRule(obj) => obj.source = source,
Element::Keyword(obj) => obj.source = source,
Element::BabelCall(obj) => obj.source = source,
Element::LatexEnvironment(obj) => obj.source = source,
}
}
}
impl<'s> GetStandardProperties<'s> for Element<'s> { impl<'s> GetStandardProperties<'s> for Element<'s> {
fn get_standard_properties<'b>(&'b self) -> &'b dyn StandardProperties<'s> { fn get_standard_properties<'b>(&'b self) -> &'b dyn StandardProperties<'s> {
match self { match self {

View File

@@ -1,3 +1,5 @@
use std::borrow::Cow;
use super::object::Object; use super::object::Object;
use super::AffiliatedKeywords; use super::AffiliatedKeywords;
use super::GetAffiliatedKeywords; use super::GetAffiliatedKeywords;
@@ -59,7 +61,7 @@ pub struct ExampleBlock<'s> {
pub retain_labels: RetainLabels, pub retain_labels: RetainLabels,
pub use_labels: bool, pub use_labels: bool,
pub label_format: Option<&'s str>, pub label_format: Option<&'s str>,
pub contents: String, pub contents: Cow<'s, str>,
} }
#[derive(Debug)] #[derive(Debug)]
@@ -68,7 +70,7 @@ pub struct ExportBlock<'s> {
pub affiliated_keywords: AffiliatedKeywords<'s>, pub affiliated_keywords: AffiliatedKeywords<'s>,
pub export_type: Option<&'s str>, pub export_type: Option<&'s str>,
pub data: Option<&'s str>, pub data: Option<&'s str>,
pub contents: String, pub contents: Cow<'s, str>,
} }
#[derive(Debug)] #[derive(Debug)]
@@ -83,7 +85,7 @@ pub struct SrcBlock<'s> {
pub retain_labels: RetainLabels, pub retain_labels: RetainLabels,
pub use_labels: bool, pub use_labels: bool,
pub label_format: Option<&'s str>, pub label_format: Option<&'s str>,
pub contents: String, pub contents: Cow<'s, str>,
} }
#[derive(Debug)] #[derive(Debug)]
@@ -169,11 +171,11 @@ impl<'s> Paragraph<'s> {
/// Generate a paragraph of the passed in text with no additional properties. /// Generate a paragraph of the passed in text with no additional properties.
/// ///
/// This is used for elements that support an "empty" content like greater blocks. /// This is used for elements that support an "empty" content like greater blocks.
pub(crate) fn of_text(input: &'s str) -> Self { pub(crate) fn of_text(source: &'s str, body: &'s str) -> Self {
Paragraph { Paragraph {
source: input, source,
affiliated_keywords: AffiliatedKeywords::default(), affiliated_keywords: AffiliatedKeywords::default(),
children: vec![Object::PlainText(PlainText { source: input })], children: vec![Object::PlainText(PlainText { source: body })],
} }
} }
} }

View File

@@ -7,14 +7,13 @@ mod greater_element;
mod lesser_element; mod lesser_element;
mod macros; mod macros;
mod object; mod object;
mod source;
mod standard_properties; mod standard_properties;
mod util; mod util;
pub use affiliated_keyword::AffiliatedKeyword; pub use affiliated_keyword::AffiliatedKeyword;
pub use affiliated_keyword::AffiliatedKeywordValue; pub use affiliated_keyword::AffiliatedKeywordValue;
pub use affiliated_keyword::AffiliatedKeywords; pub use affiliated_keyword::AffiliatedKeywords;
pub use affiliated_keyword::GetAffiliatedKeywords; pub use affiliated_keyword::GetAffiliatedKeywords;
pub(crate) use ast_node::AstNode; pub use ast_node::AstNode;
pub use document::Document; pub use document::Document;
pub use document::DocumentElement; pub use document::DocumentElement;
pub use document::Heading; pub use document::Heading;
@@ -113,5 +112,4 @@ pub use object::WarningDelay;
pub use object::WarningDelayType; pub use object::WarningDelayType;
pub use object::Year; pub use object::Year;
pub use object::YearInner; pub use object::YearInner;
pub(crate) use source::SetSource;
pub use standard_properties::StandardProperties; pub use standard_properties::StandardProperties;

View File

@@ -1,3 +0,0 @@
pub(crate) trait SetSource<'s> {
fn set_source(&mut self, source: &'s str);
}