Compare commits
43 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
503db94b2c | ||
|
|
a4381e5e39 | ||
|
|
e11de60def | ||
|
|
b2479e9de8 | ||
|
|
49d1cef7ae | ||
|
|
ba72cc1b29 | ||
|
|
c58b0e7c35 | ||
|
|
f19d262825 | ||
|
|
68f3f2e159 | ||
|
|
269e23c1b1 | ||
|
|
e111b8b9b8 | ||
|
|
353ff07420 | ||
|
|
94dec31130 | ||
|
|
cf5d3ed745 | ||
|
|
b0b287cd47 | ||
|
|
bcdf1f5e9d | ||
|
|
17d8e76e05 | ||
|
|
8db9038c53 | ||
|
|
a276ba70e0 | ||
|
|
b7442c1e92 | ||
|
|
364ba79517 | ||
|
|
47408763e5 | ||
|
|
bd187ebfe7 | ||
|
|
59cb3c2bbf | ||
|
|
44f7412a5c | ||
|
|
01464057ad | ||
|
|
0208020e3e | ||
|
|
a2f53361eb | ||
|
|
17db05c2c7 | ||
|
|
6139ea328d | ||
|
|
d20b4a410b | ||
|
|
05c64f53b1 | ||
|
|
f65d0bb82d | ||
|
|
50d2831081 | ||
|
|
bc9bd4f97b | ||
|
|
369d3e8c50 | ||
|
|
7d73eb6bd4 | ||
|
|
f59f153ee7 | ||
|
|
20c4a0f8f7 | ||
|
|
e776a051ad | ||
|
|
77e6c22ad8 | ||
|
|
c9d7251e3b | ||
|
|
8417b5fc9d |
@@ -2,7 +2,7 @@
|
||||
|
||||
[package]
|
||||
name = "organic"
|
||||
version = "0.1.11"
|
||||
version = "0.1.12"
|
||||
authors = ["Tom Alexander <tom@fizz.buzz>"]
|
||||
description = "An org-mode parser."
|
||||
edition = "2021"
|
||||
@@ -59,6 +59,7 @@ default = []
|
||||
compare = ["tokio/process", "tokio/macros"]
|
||||
foreign_document_test = ["compare", "dep:futures", "tokio/sync", "dep:walkdir", "tokio/process"]
|
||||
tracing = ["dep:opentelemetry", "dep:opentelemetry-otlp", "dep:opentelemetry-semantic-conventions", "dep:tokio", "dep:tracing", "dep:tracing-opentelemetry", "dep:tracing-subscriber"]
|
||||
event_count = []
|
||||
|
||||
# Optimized build for any sort of release.
|
||||
[profile.release-lto]
|
||||
|
||||
8
build.rs
8
build.rs
@@ -66,10 +66,6 @@ fn write_test(test_file: &mut File, test: &walkdir::DirEntry) {
|
||||
}
|
||||
|
||||
#[cfg(feature = "compare")]
|
||||
fn is_expect_fail(name: &str) -> Option<&str> {
|
||||
match name {
|
||||
"greater_element_drawer_drawer_with_headline_inside" => Some("Apparently lines with :end: become their own paragraph. This odd behavior needs to be investigated more."),
|
||||
"element_container_priority_footnote_definition_dynamic_block" => Some("Apparently broken begin lines become their own paragraph."),
|
||||
_ => None,
|
||||
}
|
||||
fn is_expect_fail(_name: &str) -> Option<&str> {
|
||||
None
|
||||
}
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
foo
|
||||
:end:
|
||||
bar
|
||||
@@ -0,0 +1,2 @@
|
||||
foo
|
||||
:end:
|
||||
58
scripts/dump_ast.bash
Executable file
58
scripts/dump_ast.bash
Executable file
@@ -0,0 +1,58 @@
|
||||
#!/usr/bin/env bash
|
||||
#
|
||||
# Dump the AST of an org-mode document from emacs
|
||||
set -euo pipefail
|
||||
IFS=$'\n\t'
|
||||
DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
|
||||
|
||||
REALPATH=$(command -v uu-realpath || command -v realpath)
|
||||
MAKE=$(command -v gmake || command -v make)
|
||||
|
||||
############## Setup #########################
|
||||
|
||||
function die {
|
||||
local status_code="$1"
|
||||
shift
|
||||
(>&2 echo "${@}")
|
||||
exit "$status_code"
|
||||
}
|
||||
|
||||
function log {
|
||||
(>&2 echo "${@}")
|
||||
}
|
||||
|
||||
############## Program #########################
|
||||
|
||||
function main {
|
||||
if [ $# -eq 0 ]; then
|
||||
dump_ast_stdin "${@}"
|
||||
else
|
||||
dump_ast_file "${@}"
|
||||
fi
|
||||
}
|
||||
|
||||
function dump_ast_stdin {
|
||||
# Until we can find a good way to encode stdin as an elisp string in bash, I cannot operate on stdin.
|
||||
die 1 "This script only works on files."
|
||||
}
|
||||
|
||||
function dump_ast_file {
|
||||
local target_file mounted_file elisp_script
|
||||
target_file=$($REALPATH "$1")
|
||||
mounted_file="/input${target_file}"
|
||||
elisp_script=$(cat <<EOF
|
||||
(progn
|
||||
(erase-buffer)
|
||||
(require 'org)
|
||||
(defun org-table-align () t)
|
||||
(find-file-read-only "${mounted_file}")
|
||||
(org-mode)
|
||||
(message "%s" (pp-to-string (org-element-parse-buffer)))
|
||||
)
|
||||
EOF
|
||||
)
|
||||
exec docker run --init --rm -i --mount type=tmpfs,destination=/tmp -v "/:/input:ro" --entrypoint "" organic-test emacs -q --no-site-file --no-splash --batch --eval "$elisp_script"
|
||||
}
|
||||
|
||||
|
||||
main "${@}"
|
||||
@@ -14,7 +14,7 @@ function main {
|
||||
additional_flags+=(--profile "$PROFILE")
|
||||
fi
|
||||
(cd "$DIR/../" && cargo build --no-default-features "${additional_flags[@]}")
|
||||
perf record --freq=2000 --call-graph dwarf --output="$DIR/../perf.data" "$DIR/../target/${PROFILE}/parse" "${@}"
|
||||
perf record --freq=70000 --call-graph dwarf --output="$DIR/../perf.data" "$DIR/../target/${PROFILE}/parse" "${@}"
|
||||
|
||||
# Convert to a format firefox will read
|
||||
# flags to consider --show-info
|
||||
|
||||
@@ -1,15 +1,27 @@
|
||||
use super::global_settings::EntityDefinition;
|
||||
|
||||
pub(crate) const DEFAULT_ORG_ELEMENT_PARSED_KEYWORDS: [&str; 1] = ["CAPTION"];
|
||||
/// Keywords that contain the standard set of objects (excluding footnote references).
|
||||
///
|
||||
/// Corresponds to org-element-parsed-keywords elisp variable.
|
||||
pub(crate) const ORG_ELEMENT_PARSED_KEYWORDS: [&str; 1] = ["CAPTION"];
|
||||
|
||||
pub(crate) const DEFAULT_ORG_ELEMENT_DUAL_KEYWORDS: [&str; 2] = ["CAPTION", "RESULTS"];
|
||||
/// Keywords that can have a secondary value in square brackets.
|
||||
///
|
||||
/// Corresponds to org-element-dual-keywords elisp variable.
|
||||
pub(crate) const ORG_ELEMENT_DUAL_KEYWORDS: [&str; 2] = ["CAPTION", "RESULTS"];
|
||||
|
||||
pub(crate) const DEFAULT_ORG_ELEMENT_AFFILIATED_KEYWORDS: [&str; 13] = [
|
||||
/// Keywords that can be affiliated with an element.
|
||||
///
|
||||
/// Corresponds to org-element-affiliated-keywords elisp variable.
|
||||
pub(crate) const ORG_ELEMENT_AFFILIATED_KEYWORDS: [&str; 13] = [
|
||||
"CAPTION", "DATA", "HEADER", "HEADERS", "LABEL", "NAME", "PLOT", "RESNAME", "RESULT",
|
||||
"RESULTS", "SOURCE", "SRCNAME", "TBLNAME",
|
||||
];
|
||||
|
||||
pub(crate) const DEFAULT_ORG_ELEMENT_KEYWORD_TRANSLATION_ALIST: [(&str, &str); 8] = [
|
||||
/// Mapping of keyword names.
|
||||
///
|
||||
/// Corresponds to org-element-keyword-translation-alist elisp variable.
|
||||
pub(crate) const ORG_ELEMENT_KEYWORD_TRANSLATION_ALIST: [(&str, &str); 8] = [
|
||||
("DATA", "NAME"),
|
||||
("LABEL", "NAME"),
|
||||
("RESNAME", "NAME"),
|
||||
|
||||
@@ -9,11 +9,9 @@ use super::list::List;
|
||||
use super::DynContextMatcher;
|
||||
use super::RefContext;
|
||||
use crate::error::CustomError;
|
||||
use crate::error::MyError;
|
||||
use crate::error::Res;
|
||||
use crate::parser::OrgSource;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub(crate) enum ContextElement<'r, 's> {
|
||||
/// Stores a parser that indicates that children should exit upon matching an exit matcher.
|
||||
ExitMatcherNode(ExitMatcherNode<'r>),
|
||||
@@ -35,15 +33,6 @@ pub(crate) struct ExitMatcherNode<'r> {
|
||||
pub(crate) class: ExitClass,
|
||||
}
|
||||
|
||||
impl<'r> std::fmt::Debug for ExitMatcherNode<'r> {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
let mut formatter = f.debug_struct("ExitMatcherNode");
|
||||
formatter.field("class", &self.class.to_string());
|
||||
formatter.finish()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub(crate) struct Context<'g, 'r, 's> {
|
||||
global_settings: &'g GlobalSettings<'g, 's>,
|
||||
tree: List<'r, &'r ContextElement<'r, 's>>,
|
||||
@@ -108,7 +97,7 @@ impl<'g, 'r, 's> Context<'g, 'r, 's> {
|
||||
pub(crate) fn check_exit_matcher(
|
||||
&'r self,
|
||||
i: OrgSource<'s>,
|
||||
) -> IResult<OrgSource<'s>, OrgSource<'s>, CustomError<OrgSource<'s>>> {
|
||||
) -> IResult<OrgSource<'s>, OrgSource<'s>, CustomError> {
|
||||
let mut current_class_filter = ExitClass::Gamma;
|
||||
for current_node in self.iter_context() {
|
||||
let context_element = current_node.get_data();
|
||||
@@ -123,7 +112,7 @@ impl<'g, 'r, 's> Context<'g, 'r, 's> {
|
||||
}
|
||||
}
|
||||
// TODO: Make this a specific error instead of just a generic MyError
|
||||
return Err(nom::Err::Error(CustomError::MyError(MyError("NoExit"))));
|
||||
return Err(nom::Err::Error(CustomError::Static("NoExit")));
|
||||
}
|
||||
|
||||
/// Indicates if elements should consume the whitespace after them.
|
||||
|
||||
@@ -1,13 +1,7 @@
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
#[derive(Copy, Clone)]
|
||||
pub(crate) enum ExitClass {
|
||||
Document = 1,
|
||||
Alpha = 2,
|
||||
Beta = 3,
|
||||
Gamma = 4,
|
||||
}
|
||||
|
||||
impl std::fmt::Display for ExitClass {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||
write!(f, "{:?}", self)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,17 +1,16 @@
|
||||
use std::fmt::Debug;
|
||||
use std::path::PathBuf;
|
||||
|
||||
#[cfg(any(feature = "compare", feature = "foreign_document_test"))]
|
||||
pub trait FileAccessInterface: Sync + Debug {
|
||||
pub trait FileAccessInterface: Sync {
|
||||
fn read_file(&self, path: &str) -> Result<String, std::io::Error>;
|
||||
}
|
||||
|
||||
#[cfg(not(any(feature = "compare", feature = "foreign_document_test")))]
|
||||
pub trait FileAccessInterface: Debug {
|
||||
pub trait FileAccessInterface {
|
||||
fn read_file(&self, path: &str) -> Result<String, std::io::Error>;
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
#[derive(Clone)]
|
||||
pub struct LocalFileAccessInterface {
|
||||
pub working_directory: Option<PathBuf>,
|
||||
}
|
||||
|
||||
@@ -5,16 +5,12 @@ use super::constants::DEFAULT_ORG_ENTITIES;
|
||||
use super::constants::DEFAULT_ORG_LINK_PARAMETERS;
|
||||
use super::FileAccessInterface;
|
||||
use super::LocalFileAccessInterface;
|
||||
use crate::context::constants::DEFAULT_ORG_ELEMENT_AFFILIATED_KEYWORDS;
|
||||
use crate::context::constants::DEFAULT_ORG_ELEMENT_DUAL_KEYWORDS;
|
||||
use crate::context::constants::DEFAULT_ORG_ELEMENT_KEYWORD_TRANSLATION_ALIST;
|
||||
use crate::context::constants::DEFAULT_ORG_ELEMENT_PARSED_KEYWORDS;
|
||||
use crate::types::IndentationLevel;
|
||||
use crate::types::Object;
|
||||
|
||||
// TODO: Ultimately, I think we'll need most of this: https://orgmode.org/manual/In_002dbuffer-Settings.html
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
#[derive(Clone)]
|
||||
pub struct GlobalSettings<'g, 's> {
|
||||
pub radio_targets: Vec<&'g Vec<Object<'s>>>,
|
||||
pub file_access: &'g dyn FileAccessInterface,
|
||||
@@ -58,26 +54,6 @@ pub struct GlobalSettings<'g, 's> {
|
||||
///
|
||||
/// Corresponds to org-entities elisp variable.
|
||||
pub entities: &'g [EntityDefinition<'s>],
|
||||
|
||||
/// Keywords that contain the standard set of objects (excluding footnote references).
|
||||
///
|
||||
/// Corresponds to org-element-parsed-keywords elisp variable.
|
||||
pub element_parsed_keywords: &'g [&'s str],
|
||||
|
||||
/// Keywords that can have a secondary value in square brackets.
|
||||
///
|
||||
/// Corresponds to org-element-dual-keywords elisp variable.
|
||||
pub element_dual_keywords: &'g [&'s str],
|
||||
|
||||
/// Keywords that can be affiliated with an element.
|
||||
///
|
||||
/// Corresponds to org-element-affiliated-keywords elisp variable.
|
||||
pub element_affiliated_keywords: &'g [&'s str],
|
||||
|
||||
/// Mapping of keyword names.
|
||||
///
|
||||
/// Corresponds to org-element-keyword-translation-alist elisp variable.
|
||||
pub element_keyword_translation_alist: &'g [(&'s str, &'s str)],
|
||||
}
|
||||
|
||||
pub const DEFAULT_TAB_WIDTH: IndentationLevel = 8;
|
||||
@@ -112,10 +88,6 @@ impl<'g, 's> GlobalSettings<'g, 's> {
|
||||
link_parameters: &DEFAULT_ORG_LINK_PARAMETERS,
|
||||
link_templates: BTreeMap::new(),
|
||||
entities: &DEFAULT_ORG_ENTITIES,
|
||||
element_parsed_keywords: &DEFAULT_ORG_ELEMENT_PARSED_KEYWORDS,
|
||||
element_dual_keywords: &DEFAULT_ORG_ELEMENT_DUAL_KEYWORDS,
|
||||
element_affiliated_keywords: &DEFAULT_ORG_ELEMENT_AFFILIATED_KEYWORDS,
|
||||
element_keyword_translation_alist: &DEFAULT_ORG_ELEMENT_KEYWORD_TRANSLATION_ALIST,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -126,7 +98,7 @@ impl<'g, 's> Default for GlobalSettings<'g, 's> {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Default)]
|
||||
#[derive(Clone, PartialEq, Default)]
|
||||
pub enum HeadlineLevelFilter {
|
||||
Odd,
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
use crate::error::Res;
|
||||
use crate::parser::OrgSource;
|
||||
|
||||
mod constants;
|
||||
pub(crate) mod constants;
|
||||
#[allow(clippy::module_inception)]
|
||||
mod context;
|
||||
mod exiting;
|
||||
|
||||
@@ -2,22 +2,20 @@ use nom::error::ErrorKind;
|
||||
use nom::error::ParseError;
|
||||
use nom::IResult;
|
||||
|
||||
pub(crate) type Res<T, U> = IResult<T, U, CustomError<T>>;
|
||||
pub(crate) type Res<T, U> = IResult<T, U, CustomError>;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum CustomError<I> {
|
||||
MyError(MyError<&'static str>),
|
||||
Nom(I, ErrorKind),
|
||||
pub enum CustomError {
|
||||
#[allow(dead_code)]
|
||||
Text(String),
|
||||
Static(&'static str),
|
||||
IO(std::io::Error),
|
||||
BoxedError(Box<dyn std::error::Error>),
|
||||
Parser(ErrorKind),
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct MyError<I>(pub(crate) I);
|
||||
|
||||
impl<I> ParseError<I> for CustomError<I> {
|
||||
fn from_error_kind(input: I, kind: ErrorKind) -> Self {
|
||||
CustomError::Nom(input, kind)
|
||||
impl<I: std::fmt::Debug> ParseError<I> for CustomError {
|
||||
fn from_error_kind(_input: I, kind: ErrorKind) -> Self {
|
||||
CustomError::Parser(kind)
|
||||
}
|
||||
|
||||
fn append(_input: I, _kind: ErrorKind, /*mut*/ other: Self) -> Self {
|
||||
@@ -26,20 +24,20 @@ impl<I> ParseError<I> for CustomError<I> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<I> From<std::io::Error> for CustomError<I> {
|
||||
impl From<std::io::Error> for CustomError {
|
||||
fn from(value: std::io::Error) -> Self {
|
||||
CustomError::IO(value)
|
||||
}
|
||||
}
|
||||
|
||||
impl<I> From<&'static str> for CustomError<I> {
|
||||
impl From<&'static str> for CustomError {
|
||||
fn from(value: &'static str) -> Self {
|
||||
CustomError::MyError(MyError(value))
|
||||
CustomError::Static(value)
|
||||
}
|
||||
}
|
||||
|
||||
impl<I> From<Box<dyn std::error::Error>> for CustomError<I> {
|
||||
fn from(value: Box<dyn std::error::Error>) -> Self {
|
||||
CustomError::BoxedError(value)
|
||||
impl From<String> for CustomError {
|
||||
fn from(value: String) -> Self {
|
||||
CustomError::Text(value)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
#[allow(clippy::module_inception)]
|
||||
mod error;
|
||||
pub(crate) use error::CustomError;
|
||||
pub(crate) use error::MyError;
|
||||
pub(crate) use error::Res;
|
||||
|
||||
43
src/event_count/database.rs
Normal file
43
src/event_count/database.rs
Normal file
@@ -0,0 +1,43 @@
|
||||
use std::collections::HashMap;
|
||||
use std::sync::Mutex;
|
||||
|
||||
use super::EventType;
|
||||
use crate::parser::OrgSource;
|
||||
|
||||
#[derive(Debug, Eq, Hash, PartialEq)]
|
||||
struct EventKey {
|
||||
event_type: EventType,
|
||||
byte_offset: usize,
|
||||
}
|
||||
|
||||
pub(crate) type EventCount = usize;
|
||||
|
||||
static GLOBAL_DATA: Mutex<Option<HashMap<EventKey, EventCount>>> = Mutex::new(None);
|
||||
|
||||
pub(crate) fn record_event(event_type: EventType, input: OrgSource<'_>) {
|
||||
let mut db = GLOBAL_DATA.lock().unwrap();
|
||||
let db = db.get_or_insert_with(HashMap::new);
|
||||
let key = EventKey {
|
||||
event_type,
|
||||
byte_offset: input.get_byte_offset(),
|
||||
};
|
||||
*db.entry(key).or_insert(0) += 1;
|
||||
}
|
||||
|
||||
pub fn report(original_document: &str) {
|
||||
let mut db = GLOBAL_DATA.lock().unwrap();
|
||||
let db = db.get_or_insert_with(HashMap::new);
|
||||
let mut results: Vec<_> = db.iter().map(|(k, v)| (k, v)).collect();
|
||||
results.sort_by_key(|(_k, v)| *v);
|
||||
// This would put the most common at the top, but that is a pain when there is already a lot of output from the parser.
|
||||
// results.sort_by(|(_ak, av), (_bk, bv)| bv.cmp(av));
|
||||
for (key, count) in results {
|
||||
println!(
|
||||
"{:?} {} character offset: {} byte offset: {}",
|
||||
key.event_type,
|
||||
count,
|
||||
original_document[..key.byte_offset].chars().count() + 1,
|
||||
key.byte_offset
|
||||
)
|
||||
}
|
||||
}
|
||||
4
src/event_count/event_type.rs
Normal file
4
src/event_count/event_type.rs
Normal file
@@ -0,0 +1,4 @@
|
||||
#[derive(Debug, Eq, Hash, PartialEq)]
|
||||
pub(crate) enum EventType {
|
||||
ElementStart,
|
||||
}
|
||||
6
src/event_count/mod.rs
Normal file
6
src/event_count/mod.rs
Normal file
@@ -0,0 +1,6 @@
|
||||
mod database;
|
||||
mod event_type;
|
||||
|
||||
pub(crate) use database::record_event;
|
||||
pub use database::report;
|
||||
pub(crate) use event_type::EventType;
|
||||
@@ -13,6 +13,8 @@ pub mod compare;
|
||||
|
||||
mod context;
|
||||
mod error;
|
||||
#[cfg(feature = "event_count")]
|
||||
pub mod event_count;
|
||||
mod iter;
|
||||
pub mod parser;
|
||||
pub mod types;
|
||||
|
||||
@@ -54,8 +54,11 @@ fn read_stdin_to_string() -> Result<String, Box<dyn std::error::Error>> {
|
||||
}
|
||||
|
||||
fn run_anonymous_parse<P: AsRef<str>>(org_contents: P) -> Result<(), Box<dyn std::error::Error>> {
|
||||
let rust_parsed = parse(org_contents.as_ref())?;
|
||||
let org_contents = org_contents.as_ref();
|
||||
let rust_parsed = parse(org_contents)?;
|
||||
println!("{:#?}", rust_parsed);
|
||||
#[cfg(feature = "event_count")]
|
||||
organic::event_count::report(org_contents);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -75,5 +78,7 @@ fn run_parse_on_file<P: AsRef<Path>>(org_path: P) -> Result<(), Box<dyn std::err
|
||||
};
|
||||
let rust_parsed = parse_with_settings(org_contents, &global_settings)?;
|
||||
println!("{:#?}", rust_parsed);
|
||||
#[cfg(feature = "event_count")]
|
||||
organic::event_count::report(org_contents);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -14,17 +14,46 @@ use nom::multi::many0;
|
||||
use nom::multi::many_till;
|
||||
use nom::sequence::tuple;
|
||||
|
||||
use super::keyword::affiliated_keyword;
|
||||
use super::object_parser::standard_set_object;
|
||||
use super::util::confine_context;
|
||||
use super::OrgSource;
|
||||
use crate::context::bind_context;
|
||||
use crate::context::constants::ORG_ELEMENT_DUAL_KEYWORDS;
|
||||
use crate::context::constants::ORG_ELEMENT_KEYWORD_TRANSLATION_ALIST;
|
||||
use crate::context::constants::ORG_ELEMENT_PARSED_KEYWORDS;
|
||||
use crate::context::Context;
|
||||
use crate::context::ContextElement;
|
||||
use crate::context::GlobalSettings;
|
||||
use crate::context::List;
|
||||
use crate::error::Res;
|
||||
use crate::types::AffiliatedKeywordValue;
|
||||
use crate::types::AffiliatedKeywords;
|
||||
use crate::types::Keyword;
|
||||
|
||||
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
|
||||
pub(crate) fn affiliated_keywords<'s>(
|
||||
input: OrgSource<'s>,
|
||||
) -> Res<OrgSource<'s>, Vec<Keyword<'s>>> {
|
||||
let mut ret = Vec::new();
|
||||
let mut remaining = input;
|
||||
|
||||
loop {
|
||||
let result = affiliated_keyword(remaining);
|
||||
match result {
|
||||
Ok((remain, kw)) => {
|
||||
remaining = remain;
|
||||
ret.push(kw);
|
||||
}
|
||||
Err(_) => {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok((remaining, ret))
|
||||
}
|
||||
|
||||
pub(crate) fn parse_affiliated_keywords<'g, 's, AK>(
|
||||
global_settings: &'g GlobalSettings<'g, 's>,
|
||||
input: AK,
|
||||
@@ -34,8 +63,8 @@ where
|
||||
{
|
||||
let mut ret = BTreeMap::new();
|
||||
for kw in input {
|
||||
let translated_name = translate_name(global_settings, kw.key);
|
||||
let keyword_type = identify_keyword_type(global_settings, translated_name.as_str());
|
||||
let translated_name = translate_name(kw.key);
|
||||
let keyword_type = identify_keyword_type(translated_name.as_str());
|
||||
match keyword_type {
|
||||
AffiliatedKeywordType::SingleString => {
|
||||
ret.insert(
|
||||
@@ -120,12 +149,12 @@ where
|
||||
AffiliatedKeywords { keywords: ret }
|
||||
}
|
||||
|
||||
fn translate_name<'g, 's>(global_settings: &'g GlobalSettings<'g, 's>, name: &'s str) -> String {
|
||||
fn translate_name(name: &str) -> String {
|
||||
let name_until_optval = name
|
||||
.split_once('[')
|
||||
.map(|(before, _after)| before)
|
||||
.unwrap_or(name);
|
||||
for (src, dst) in global_settings.element_keyword_translation_alist {
|
||||
for (src, dst) in ORG_ELEMENT_KEYWORD_TRANSLATION_ALIST {
|
||||
if name_until_optval.eq_ignore_ascii_case(src) {
|
||||
return dst.to_lowercase();
|
||||
}
|
||||
@@ -140,20 +169,15 @@ enum AffiliatedKeywordType {
|
||||
ObjectTree,
|
||||
}
|
||||
|
||||
fn identify_keyword_type<'g, 's>(
|
||||
global_settings: &'g GlobalSettings<'g, 's>,
|
||||
name: &'s str,
|
||||
) -> AffiliatedKeywordType {
|
||||
fn identify_keyword_type(name: &str) -> AffiliatedKeywordType {
|
||||
let is_multiple = ["CAPTION", "HEADER"]
|
||||
.into_iter()
|
||||
.any(|candidate| name.eq_ignore_ascii_case(candidate))
|
||||
|| name.to_lowercase().starts_with("attr_");
|
||||
let is_parsed = global_settings
|
||||
.element_parsed_keywords
|
||||
let is_parsed = ORG_ELEMENT_PARSED_KEYWORDS
|
||||
.iter()
|
||||
.any(|candidate| name.eq_ignore_ascii_case(candidate));
|
||||
let can_have_optval = global_settings
|
||||
.element_dual_keywords
|
||||
let can_have_optval = ORG_ELEMENT_DUAL_KEYWORDS
|
||||
.iter()
|
||||
.any(|candidate| name.eq_ignore_ascii_case(candidate));
|
||||
match (is_multiple, is_parsed, can_have_optval) {
|
||||
|
||||
@@ -21,7 +21,6 @@ use super::OrgSource;
|
||||
use crate::context::Matcher;
|
||||
use crate::context::RefContext;
|
||||
use crate::error::CustomError;
|
||||
use crate::error::MyError;
|
||||
use crate::error::Res;
|
||||
use crate::parser::util::get_consumed;
|
||||
use crate::parser::util::org_line_ending;
|
||||
@@ -217,9 +216,7 @@ fn impl_balanced_bracket<
|
||||
}
|
||||
|
||||
if fail_parser(remaining).is_ok() {
|
||||
return Err(nom::Err::Error(CustomError::MyError(MyError(
|
||||
"Fail parser matched.",
|
||||
))));
|
||||
return Err(nom::Err::Error(CustomError::Static("Fail parser matched.")));
|
||||
}
|
||||
|
||||
let (remain, _) = anychar(remaining)?;
|
||||
|
||||
165
src/parser/bullshitium.rs
Normal file
165
src/parser/bullshitium.rs
Normal file
@@ -0,0 +1,165 @@
|
||||
use nom::branch::alt;
|
||||
use nom::bytes::complete::tag_no_case;
|
||||
use nom::character::complete::anychar;
|
||||
use nom::character::complete::space0;
|
||||
use nom::multi::many_till;
|
||||
use nom::sequence::tuple;
|
||||
|
||||
use super::paragraph::paragraph;
|
||||
use super::util::maybe_consume_trailing_whitespace_if_not_exiting;
|
||||
use super::util::org_line_ending;
|
||||
use super::util::start_of_line;
|
||||
use super::OrgSource;
|
||||
use crate::context::bind_context;
|
||||
use crate::context::RefContext;
|
||||
use crate::error::CustomError;
|
||||
use crate::error::Res;
|
||||
use crate::parser::macros::element;
|
||||
use crate::types::AffiliatedKeywords;
|
||||
use crate::types::Object;
|
||||
use crate::types::Paragraph;
|
||||
use crate::types::PlainText;
|
||||
|
||||
#[cfg_attr(
|
||||
feature = "tracing",
|
||||
tracing::instrument(ret, level = "debug", skip(context))
|
||||
)]
|
||||
pub(crate) fn bullshitium<'b, 'g, 'r, 's>(
|
||||
context: RefContext<'b, 'g, 'r, 's>,
|
||||
input: OrgSource<'s>,
|
||||
) -> Res<OrgSource<'s>, Paragraph<'s>> {
|
||||
alt((
|
||||
bind_context!(broken_end, context),
|
||||
bind_context!(broken_dynamic_block, context),
|
||||
))(input)
|
||||
}
|
||||
|
||||
#[cfg_attr(
|
||||
feature = "tracing",
|
||||
tracing::instrument(ret, level = "debug", skip(context))
|
||||
)]
|
||||
pub(crate) fn detect_bullshitium<'b, 'g, 'r, 's>(
|
||||
context: RefContext<'b, 'g, 'r, 's>,
|
||||
input: OrgSource<'s>,
|
||||
) -> Res<OrgSource<'s>, ()> {
|
||||
element!(detect_broken_end, context, input);
|
||||
element!(detect_broken_dynamic_block, context, input);
|
||||
Err(nom::Err::Error(CustomError::Static("No bullshitium.")))
|
||||
}
|
||||
|
||||
#[cfg_attr(
|
||||
feature = "tracing",
|
||||
tracing::instrument(ret, level = "debug", skip(context))
|
||||
)]
|
||||
pub(crate) fn broken_end<'b, 'g, 'r, 's>(
|
||||
context: RefContext<'b, 'g, 'r, 's>,
|
||||
input: OrgSource<'s>,
|
||||
) -> Res<OrgSource<'s>, Paragraph<'s>> {
|
||||
start_of_line(input)?;
|
||||
let (remaining, _) = space0(input)?;
|
||||
let (remaining, _) = tag_no_case(":end:")(remaining)?;
|
||||
let (lead_in_remaining, _) = tuple((space0, org_line_ending))(remaining)?;
|
||||
if let Ok((remaining, mut paragraph)) =
|
||||
paragraph(std::iter::empty(), lead_in_remaining, context, input)
|
||||
{
|
||||
match paragraph.children.first_mut() {
|
||||
Some(Object::PlainText(plain_text)) => {
|
||||
plain_text.source = input.get_until_end_of_str(plain_text.source).into();
|
||||
}
|
||||
Some(obj) => {
|
||||
panic!("Unhandled first object type inside bullshitium {:?}", obj);
|
||||
}
|
||||
None => {
|
||||
unreachable!("Paragraph must have children.");
|
||||
}
|
||||
};
|
||||
Ok((remaining, paragraph))
|
||||
} else {
|
||||
let (remaining, _trailing_ws) =
|
||||
maybe_consume_trailing_whitespace_if_not_exiting(context, lead_in_remaining)?;
|
||||
|
||||
Ok((
|
||||
remaining,
|
||||
Paragraph {
|
||||
source: input.get_until(remaining).into(),
|
||||
affiliated_keywords: AffiliatedKeywords::default(),
|
||||
children: vec![Object::PlainText(PlainText {
|
||||
source: input.get_until(lead_in_remaining).into(),
|
||||
})],
|
||||
},
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg_attr(
|
||||
feature = "tracing",
|
||||
tracing::instrument(ret, level = "debug", skip(_context))
|
||||
)]
|
||||
pub(crate) fn detect_broken_end<'b, 'g, 'r, 's>(
|
||||
_context: RefContext<'b, 'g, 'r, 's>,
|
||||
input: OrgSource<'s>,
|
||||
) -> Res<OrgSource<'s>, ()> {
|
||||
start_of_line(input)?;
|
||||
let (remaining, _) = space0(input)?;
|
||||
let (remaining, _) = tag_no_case(":end:")(remaining)?;
|
||||
let (_remaining, _) = tuple((space0, org_line_ending))(remaining)?;
|
||||
Ok((input, ()))
|
||||
}
|
||||
|
||||
#[cfg_attr(
|
||||
feature = "tracing",
|
||||
tracing::instrument(ret, level = "debug", skip(context))
|
||||
)]
|
||||
pub(crate) fn broken_dynamic_block<'b, 'g, 'r, 's>(
|
||||
context: RefContext<'b, 'g, 'r, 's>,
|
||||
input: OrgSource<'s>,
|
||||
) -> Res<OrgSource<'s>, Paragraph<'s>> {
|
||||
start_of_line(input)?;
|
||||
let (remaining, _) = space0(input)?;
|
||||
let (remaining, _) = tag_no_case("#+BEGIN:")(remaining)?;
|
||||
let (lead_in_remaining, _) = many_till(anychar, org_line_ending)(remaining)?;
|
||||
if let Ok((remaining, mut paragraph)) =
|
||||
paragraph(std::iter::empty(), lead_in_remaining, context, input)
|
||||
{
|
||||
match paragraph.children.first_mut() {
|
||||
Some(Object::PlainText(plain_text)) => {
|
||||
plain_text.source = input.get_until_end_of_str(plain_text.source).into();
|
||||
}
|
||||
Some(obj) => {
|
||||
panic!("Unhandled first object type inside bullshitium {:?}", obj);
|
||||
}
|
||||
None => {
|
||||
unreachable!("Paragraph must have children.");
|
||||
}
|
||||
};
|
||||
Ok((remaining, paragraph))
|
||||
} else {
|
||||
let (remaining, _trailing_ws) =
|
||||
maybe_consume_trailing_whitespace_if_not_exiting(context, lead_in_remaining)?;
|
||||
|
||||
Ok((
|
||||
remaining,
|
||||
Paragraph {
|
||||
source: input.get_until(remaining).into(),
|
||||
affiliated_keywords: AffiliatedKeywords::default(),
|
||||
children: vec![Object::PlainText(PlainText {
|
||||
source: input.get_until(lead_in_remaining).into(),
|
||||
})],
|
||||
},
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg_attr(
|
||||
feature = "tracing",
|
||||
tracing::instrument(ret, level = "debug", skip(_context))
|
||||
)]
|
||||
pub(crate) fn detect_broken_dynamic_block<'b, 'g, 'r, 's>(
|
||||
_context: RefContext<'b, 'g, 'r, 's>,
|
||||
input: OrgSource<'s>,
|
||||
) -> Res<OrgSource<'s>, ()> {
|
||||
start_of_line(input)?;
|
||||
let (remaining, _) = space0(input)?;
|
||||
let (_remaining, _) = tag_no_case("#+BEGIN:")(remaining)?;
|
||||
Ok((input, ()))
|
||||
}
|
||||
@@ -137,7 +137,7 @@ fn _global_prefix_end<'b, 'g, 'r, 's>(
|
||||
unreachable!("Exceeded citation global prefix bracket depth.")
|
||||
}
|
||||
if current_depth == 0 {
|
||||
let close_bracket = tag::<&str, OrgSource<'_>, CustomError<OrgSource<'_>>>("]")(input);
|
||||
let close_bracket = tag::<_, _, CustomError>("]")(input);
|
||||
if close_bracket.is_ok() {
|
||||
return close_bracket;
|
||||
}
|
||||
@@ -191,7 +191,7 @@ fn _global_suffix_end<'b, 'g, 'r, 's>(
|
||||
unreachable!("Exceeded citation global suffix bracket depth.")
|
||||
}
|
||||
if current_depth == 0 {
|
||||
let close_bracket = tag::<&str, OrgSource<'_>, CustomError<OrgSource<'_>>>("]")(input);
|
||||
let close_bracket = tag::<_, _, CustomError>("]")(input);
|
||||
if close_bracket.is_ok() {
|
||||
return close_bracket;
|
||||
}
|
||||
|
||||
@@ -20,7 +20,6 @@ use crate::context::ExitClass;
|
||||
use crate::context::ExitMatcherNode;
|
||||
use crate::context::RefContext;
|
||||
use crate::error::CustomError;
|
||||
use crate::error::MyError;
|
||||
use crate::error::Res;
|
||||
use crate::parser::object_parser::minimal_set_object;
|
||||
use crate::parser::util::exit_matcher_parser;
|
||||
@@ -151,7 +150,7 @@ fn _key_prefix_end<'b, 'g, 'r, 's>(
|
||||
unreachable!("Exceeded citation key prefix bracket depth.")
|
||||
}
|
||||
if current_depth == 0 {
|
||||
let close_bracket = tag::<&str, OrgSource<'_>, CustomError<OrgSource<'_>>>("]")(input);
|
||||
let close_bracket = tag::<_, _, CustomError>("]")(input);
|
||||
if close_bracket.is_ok() {
|
||||
return close_bracket;
|
||||
}
|
||||
@@ -181,7 +180,7 @@ fn _key_suffix_end<'b, 'g, 'r, 's>(
|
||||
unreachable!("Exceeded citation key suffix bracket depth.")
|
||||
}
|
||||
if current_depth == 0 {
|
||||
let close_bracket = tag::<&str, OrgSource<'_>, CustomError<OrgSource<'_>>>("]")(input);
|
||||
let close_bracket = tag::<_, _, CustomError>("]")(input);
|
||||
if close_bracket.is_ok() {
|
||||
return close_bracket;
|
||||
}
|
||||
@@ -199,9 +198,7 @@ where
|
||||
let pre_bracket_depth = input.get_bracket_depth();
|
||||
let (remaining, output) = inner(input)?;
|
||||
if remaining.get_bracket_depth() - pre_bracket_depth != 0 {
|
||||
return Err(nom::Err::Error(CustomError::MyError(MyError(
|
||||
"UnbalancedBrackets",
|
||||
))));
|
||||
return Err(nom::Err::Error(CustomError::Static("UnbalancedBrackets")));
|
||||
}
|
||||
Ok((remaining, output))
|
||||
}
|
||||
|
||||
@@ -19,7 +19,6 @@ use crate::context::parser_with_context;
|
||||
use crate::context::ContextElement;
|
||||
use crate::context::RefContext;
|
||||
use crate::error::CustomError;
|
||||
use crate::error::MyError;
|
||||
use crate::error::Res;
|
||||
use crate::parser::util::exit_matcher_parser;
|
||||
use crate::parser::util::immediate_in_section;
|
||||
@@ -35,9 +34,9 @@ pub(crate) fn comment<'b, 'g, 'r, 's>(
|
||||
input: OrgSource<'s>,
|
||||
) -> Res<OrgSource<'s>, Comment<'s>> {
|
||||
if immediate_in_section(context, "comment") {
|
||||
return Err(nom::Err::Error(CustomError::MyError(MyError(
|
||||
return Err(nom::Err::Error(CustomError::Static(
|
||||
"Cannot nest objects of the same element",
|
||||
))));
|
||||
)));
|
||||
}
|
||||
let parser_context = ContextElement::Context("comment");
|
||||
let parser_context = context.with_additional_node(&parser_context);
|
||||
|
||||
@@ -19,9 +19,7 @@ use crate::context::GlobalSettings;
|
||||
use crate::context::List;
|
||||
use crate::context::RefContext;
|
||||
use crate::error::CustomError;
|
||||
use crate::error::MyError;
|
||||
use crate::error::Res;
|
||||
use crate::parser::org_source::convert_error;
|
||||
use crate::parser::util::blank_line;
|
||||
use crate::types::AstNode;
|
||||
use crate::types::Document;
|
||||
@@ -103,7 +101,7 @@ pub fn parse_file_with_settings<'g, 's, P: AsRef<Path>>(
|
||||
/// This will not prevent additional settings from being learned during parsing, for example when encountering a "#+TODO".
|
||||
#[allow(dead_code)]
|
||||
fn document<'s>(context: RefContext<'_, '_, '_, 's>, input: &'s str) -> Res<&'s str, Document<'s>> {
|
||||
let (remaining, doc) = document_org_source(context, input.into()).map_err(convert_error)?;
|
||||
let (remaining, doc) = document_org_source(context, input.into())?;
|
||||
Ok((Into::<&str>::into(remaining), doc))
|
||||
}
|
||||
|
||||
@@ -127,27 +125,16 @@ fn document_org_source<'b, 'g, 'r, 's>(
|
||||
.get_global_settings()
|
||||
.file_access
|
||||
.read_file(setup_file)
|
||||
.map_err(|err| nom::Err::<CustomError<OrgSource<'_>>>::Failure(err.into()))
|
||||
.map_err(|err| nom::Err::<CustomError>::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| {
|
||||
eprintln!("{}", err);
|
||||
nom::Err::Error(CustomError::MyError(MyError(
|
||||
"TODO: make this take an owned string so I can dump err.to_string() into it.",
|
||||
)))
|
||||
})?;
|
||||
let (_, setup_file_settings) = scan_for_in_buffer_settings(setup_file.into())?;
|
||||
final_settings.extend(setup_file_settings);
|
||||
}
|
||||
final_settings.extend(document_settings);
|
||||
let new_settings = apply_in_buffer_settings(final_settings, context.get_global_settings())
|
||||
.map_err(|err| {
|
||||
eprintln!("{}", err);
|
||||
nom::Err::Error(CustomError::MyError(MyError(
|
||||
"TODO: make this take an owned string so I can dump err.to_string() into it.",
|
||||
)))
|
||||
})?;
|
||||
.map_err(nom::Err::Error)?;
|
||||
let new_context = context.with_global_settings(&new_settings);
|
||||
let context = &new_context;
|
||||
|
||||
@@ -172,15 +159,13 @@ fn document_org_source<'b, 'g, 'r, 's>(
|
||||
let parser_context = context.with_global_settings(&new_global_settings);
|
||||
let (remaining, mut document) = _document(&parser_context, input)
|
||||
.map(|(rem, out)| (Into::<&str>::into(rem), out))?;
|
||||
apply_post_parse_in_buffer_settings(&mut document)
|
||||
.map_err(|err| nom::Err::<CustomError<OrgSource<'_>>>::Failure(err.into()))?;
|
||||
apply_post_parse_in_buffer_settings(&mut document);
|
||||
return Ok((remaining.into(), document));
|
||||
}
|
||||
}
|
||||
|
||||
// Find final in-buffer settings that do not impact parsing
|
||||
apply_post_parse_in_buffer_settings(&mut document)
|
||||
.map_err(|err| nom::Err::<CustomError<OrgSource<'_>>>::Failure(err.into()))?;
|
||||
apply_post_parse_in_buffer_settings(&mut document);
|
||||
|
||||
Ok((remaining.into(), document))
|
||||
}
|
||||
@@ -210,3 +195,17 @@ fn _document<'b, 'g, 'r, 's>(
|
||||
},
|
||||
))
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use test::Bencher;
|
||||
|
||||
use super::*;
|
||||
|
||||
#[bench]
|
||||
fn bench_full_document(b: &mut Bencher) {
|
||||
let input = include_str!("../../org_mode_samples/element_container_priority/README.org");
|
||||
|
||||
b.iter(|| assert!(parse(input).is_ok()));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,7 +19,6 @@ use crate::context::ExitClass;
|
||||
use crate::context::ExitMatcherNode;
|
||||
use crate::context::RefContext;
|
||||
use crate::error::CustomError;
|
||||
use crate::error::MyError;
|
||||
use crate::error::Res;
|
||||
use crate::parser::element_parser::element;
|
||||
use crate::parser::util::blank_line;
|
||||
@@ -48,9 +47,9 @@ where
|
||||
AK: IntoIterator<Item = Keyword<'s>>,
|
||||
{
|
||||
if immediate_in_section(context, "drawer") {
|
||||
return Err(nom::Err::Error(CustomError::MyError(MyError(
|
||||
return Err(nom::Err::Error(CustomError::Static(
|
||||
"Cannot nest objects of the same element",
|
||||
))));
|
||||
)));
|
||||
}
|
||||
start_of_line(remaining)?;
|
||||
let (remaining, _leading_whitespace) = space0(remaining)?;
|
||||
|
||||
@@ -26,7 +26,6 @@ use crate::context::ExitClass;
|
||||
use crate::context::ExitMatcherNode;
|
||||
use crate::context::RefContext;
|
||||
use crate::error::CustomError;
|
||||
use crate::error::MyError;
|
||||
use crate::error::Res;
|
||||
use crate::parser::element_parser::element;
|
||||
use crate::parser::util::blank_line;
|
||||
@@ -54,9 +53,9 @@ where
|
||||
AK: IntoIterator<Item = Keyword<'s>>,
|
||||
{
|
||||
if immediate_in_section(context, "dynamic block") {
|
||||
return Err(nom::Err::Error(CustomError::MyError(MyError(
|
||||
return Err(nom::Err::Error(CustomError::Static(
|
||||
"Cannot nest objects of the same element",
|
||||
))));
|
||||
)));
|
||||
}
|
||||
|
||||
start_of_line(remaining)?;
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
use nom::multi::many0;
|
||||
|
||||
use super::babel_call::babel_call;
|
||||
use super::clock::clock;
|
||||
use super::comment::comment;
|
||||
@@ -14,7 +12,6 @@ use super::footnote_definition::detect_footnote_definition;
|
||||
use super::footnote_definition::footnote_definition;
|
||||
use super::greater_block::greater_block;
|
||||
use super::horizontal_rule::horizontal_rule;
|
||||
use super::keyword::affiliated_keyword;
|
||||
use super::keyword::keyword;
|
||||
use super::latex_environment::latex_environment;
|
||||
use super::lesser_block::comment_block;
|
||||
@@ -27,11 +24,16 @@ use super::paragraph::paragraph;
|
||||
use super::plain_list::detect_plain_list;
|
||||
use super::plain_list::plain_list;
|
||||
use super::table::detect_table;
|
||||
use crate::context::parser_with_context;
|
||||
use crate::context::RefContext;
|
||||
use crate::error::CustomError;
|
||||
use crate::error::MyError;
|
||||
use crate::error::Res;
|
||||
#[cfg(feature = "event_count")]
|
||||
use crate::event_count::record_event;
|
||||
#[cfg(feature = "event_count")]
|
||||
use crate::event_count::EventType;
|
||||
use crate::parser::affiliated_keyword::affiliated_keywords;
|
||||
use crate::parser::bullshitium::bullshitium;
|
||||
use crate::parser::bullshitium::detect_bullshitium;
|
||||
use crate::parser::macros::ak_element;
|
||||
use crate::parser::macros::element;
|
||||
use crate::parser::table::org_mode_table;
|
||||
@@ -55,8 +57,9 @@ fn _element<'b, 'g, 'r, 's>(
|
||||
input: OrgSource<'s>,
|
||||
can_be_paragraph: bool,
|
||||
) -> Res<OrgSource<'s>, Element<'s>> {
|
||||
let (post_affiliated_keywords_input, affiliated_keywords) =
|
||||
many0(parser_with_context!(affiliated_keyword)(context))(input)?;
|
||||
#[cfg(feature = "event_count")]
|
||||
record_event(EventType::ElementStart, input);
|
||||
let (post_affiliated_keywords_input, affiliated_keywords) = affiliated_keywords(input)?;
|
||||
|
||||
let mut affiliated_keywords = affiliated_keywords.into_iter();
|
||||
|
||||
@@ -240,6 +243,9 @@ fn _element<'b, 'g, 'r, 's>(
|
||||
);
|
||||
|
||||
if can_be_paragraph {
|
||||
// Fake paragraphs
|
||||
element!(bullshitium, context, input, Element::Paragraph);
|
||||
|
||||
// Paragraph without affiliated keyword
|
||||
ak_element!(
|
||||
paragraph,
|
||||
@@ -251,9 +257,7 @@ fn _element<'b, 'g, 'r, 's>(
|
||||
);
|
||||
}
|
||||
|
||||
Err(nom::Err::Error(CustomError::MyError(MyError(
|
||||
"No element.",
|
||||
))))
|
||||
Err(nom::Err::Error(CustomError::Static("No element.")))
|
||||
}
|
||||
|
||||
pub(crate) const fn detect_element(
|
||||
@@ -272,8 +276,7 @@ fn _detect_element<'b, 'g, 'r, 's>(
|
||||
input: OrgSource<'s>,
|
||||
can_be_paragraph: bool,
|
||||
) -> Res<OrgSource<'s>, ()> {
|
||||
let (post_affiliated_keywords_input, affiliated_keywords) =
|
||||
many0(parser_with_context!(affiliated_keyword)(context))(input)?;
|
||||
let (post_affiliated_keywords_input, affiliated_keywords) = affiliated_keywords(input)?;
|
||||
|
||||
let mut affiliated_keywords = affiliated_keywords.into_iter();
|
||||
|
||||
@@ -319,11 +322,14 @@ fn _detect_element<'b, 'g, 'r, 's>(
|
||||
input
|
||||
);
|
||||
|
||||
// Fake paragraphs
|
||||
if !can_be_paragraph {
|
||||
element!(detect_bullshitium, context, input);
|
||||
}
|
||||
|
||||
if _element(context, input, can_be_paragraph).is_ok() {
|
||||
return Ok((input, ()));
|
||||
}
|
||||
|
||||
Err(nom::Err::Error(CustomError::MyError(MyError(
|
||||
"No element detected.",
|
||||
))))
|
||||
Err(nom::Err::Error(CustomError::Static("No element detected.")))
|
||||
}
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
use nom::branch::alt;
|
||||
use nom::bytes::complete::tag;
|
||||
use nom::character::complete::satisfy;
|
||||
use nom::combinator::cond;
|
||||
use nom::combinator::eof;
|
||||
use nom::combinator::map;
|
||||
use nom::combinator::peek;
|
||||
use nom::combinator::recognize;
|
||||
use nom::combinator::verify;
|
||||
use nom::sequence::tuple;
|
||||
|
||||
use super::org_source::OrgSource;
|
||||
@@ -13,7 +13,6 @@ use super::util::maybe_consume_object_trailing_whitespace_if_not_exiting;
|
||||
use crate::context::EntityDefinition;
|
||||
use crate::context::RefContext;
|
||||
use crate::error::CustomError;
|
||||
use crate::error::MyError;
|
||||
use crate::error::Res;
|
||||
use crate::parser::util::get_consumed;
|
||||
use crate::types::Entity;
|
||||
@@ -58,18 +57,21 @@ fn name<'b, 'g, 'r, 's>(
|
||||
) -> Res<OrgSource<'s>, (&'g EntityDefinition<'s>, OrgSource<'s>, bool)> {
|
||||
for entity in context.get_global_settings().entities {
|
||||
let result = tuple((
|
||||
tag::<_, _, CustomError<_>>(entity.name),
|
||||
tag::<_, _, CustomError>(entity.name),
|
||||
cond(
|
||||
!entity.name.ends_with(' '),
|
||||
alt((
|
||||
verify(map(tag("{}"), |_| true), |_| !entity.name.ends_with(' ')),
|
||||
map(tag("{}"), |_| true),
|
||||
map(peek(recognize(entity_end)), |_| false),
|
||||
)),
|
||||
),
|
||||
))(input);
|
||||
if let Ok((remaining, (ent, use_brackets))) = result {
|
||||
return Ok((remaining, (entity, ent, use_brackets)));
|
||||
return Ok((remaining, (entity, ent, use_brackets.unwrap_or(false))));
|
||||
}
|
||||
}
|
||||
|
||||
Err(nom::Err::Error(CustomError::MyError(MyError("NoEntity"))))
|
||||
Err(nom::Err::Error(CustomError::Static("NoEntity")))
|
||||
}
|
||||
|
||||
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
|
||||
|
||||
@@ -22,7 +22,6 @@ use crate::context::ExitClass;
|
||||
use crate::context::ExitMatcherNode;
|
||||
use crate::context::RefContext;
|
||||
use crate::error::CustomError;
|
||||
use crate::error::MyError;
|
||||
use crate::error::Res;
|
||||
use crate::parser::element_parser::element;
|
||||
use crate::parser::util::blank_line;
|
||||
@@ -48,9 +47,9 @@ where
|
||||
AK: IntoIterator<Item = Keyword<'s>>,
|
||||
{
|
||||
if immediate_in_section(context, "footnote definition") {
|
||||
return Err(nom::Err::Error(CustomError::MyError(MyError(
|
||||
return Err(nom::Err::Error(CustomError::Static(
|
||||
"Cannot nest objects of the same element",
|
||||
))));
|
||||
)));
|
||||
}
|
||||
start_of_line(remaining)?;
|
||||
// Cannot be indented.
|
||||
|
||||
@@ -20,7 +20,6 @@ use crate::context::ExitMatcherNode;
|
||||
use crate::context::List;
|
||||
use crate::context::RefContext;
|
||||
use crate::error::CustomError;
|
||||
use crate::error::MyError;
|
||||
use crate::error::Res;
|
||||
use crate::parser::footnote_definition::label;
|
||||
use crate::parser::object_parser::standard_set_object;
|
||||
@@ -176,9 +175,9 @@ fn _footnote_definition_end<'b, 'g, 'r, 's>(
|
||||
let current_depth = input.get_bracket_depth() - starting_bracket_depth;
|
||||
if current_depth > 0 {
|
||||
// Its impossible for the next character to end the footnote reference definition if we're any amount of brackets deep
|
||||
return Err(nom::Err::Error(CustomError::MyError(MyError(
|
||||
return Err(nom::Err::Error(CustomError::Static(
|
||||
"NoFootnoteReferenceDefinitionEnd",
|
||||
))));
|
||||
)));
|
||||
}
|
||||
if current_depth < 0 {
|
||||
// This shouldn't be possible because if depth is 0 then a closing bracket should end the footnote definition.
|
||||
|
||||
@@ -28,7 +28,6 @@ use crate::context::ExitClass;
|
||||
use crate::context::ExitMatcherNode;
|
||||
use crate::context::RefContext;
|
||||
use crate::error::CustomError;
|
||||
use crate::error::MyError;
|
||||
use crate::error::Res;
|
||||
use crate::parser::element_parser::element;
|
||||
use crate::parser::util::blank_line;
|
||||
@@ -232,9 +231,9 @@ fn greater_block_body<'c, 'b, 'g, 'r, 's>(
|
||||
context_name: &'c str,
|
||||
) -> Res<OrgSource<'s>, (&'s str, Vec<Element<'s>>)> {
|
||||
if in_section(context, context_name) {
|
||||
return Err(nom::Err::Error(CustomError::MyError(MyError(
|
||||
return Err(nom::Err::Error(CustomError::Static(
|
||||
"Cannot nest objects of the same element",
|
||||
))));
|
||||
)));
|
||||
}
|
||||
let exit_with_name = greater_block_end(name);
|
||||
let (remaining, _nl) = tuple((space0, line_ending))(input)?;
|
||||
|
||||
@@ -18,18 +18,18 @@ use nom::sequence::tuple;
|
||||
|
||||
use super::org_source::OrgSource;
|
||||
use super::section::section;
|
||||
use super::util::exit_matcher_parser;
|
||||
use super::util::get_consumed;
|
||||
use super::util::org_line_ending;
|
||||
use super::util::org_space;
|
||||
use super::util::org_space_or_line_ending;
|
||||
use super::util::start_of_line;
|
||||
use crate::context::parser_with_context;
|
||||
use crate::context::bind_context;
|
||||
use crate::context::ContextElement;
|
||||
use crate::context::ExitClass;
|
||||
use crate::context::ExitMatcherNode;
|
||||
use crate::context::RefContext;
|
||||
use crate::error::CustomError;
|
||||
use crate::error::MyError;
|
||||
use crate::error::Res;
|
||||
use crate::parser::object_parser::standard_set_object;
|
||||
use crate::parser::util::blank_line;
|
||||
@@ -62,10 +62,10 @@ fn _heading<'b, 'g, 'r, 's>(
|
||||
let mut scheduled = None;
|
||||
let mut deadline = None;
|
||||
let mut closed = None;
|
||||
not(|i| context.check_exit_matcher(i))(input)?;
|
||||
not(bind_context!(exit_matcher_parser, context))(input)?;
|
||||
let (remaining, pre_headline) = headline(context, input, parent_star_count)?;
|
||||
let section_matcher = parser_with_context!(section)(context);
|
||||
let heading_matcher = parser_with_context!(heading(pre_headline.star_count))(context);
|
||||
let section_matcher = bind_context!(section, context);
|
||||
let heading_matcher = bind_context!(heading(pre_headline.star_count), context);
|
||||
let (remaining, maybe_section) =
|
||||
opt(map(section_matcher, DocumentElement::Section))(remaining)?;
|
||||
let (remaining, _ws) = opt(tuple((start_of_line, many0(blank_line))))(remaining)?;
|
||||
@@ -155,7 +155,7 @@ fn headline<'b, 'g, 'r, 's>(
|
||||
let (remaining, (_, (headline_level, star_count, _), _)) = tuple((
|
||||
start_of_line,
|
||||
verify(
|
||||
parser_with_context!(headline_level)(&parser_context),
|
||||
bind_context!(headline_level, &parser_context),
|
||||
|(_, count, _)| *count > parent_star_count,
|
||||
),
|
||||
peek(org_space),
|
||||
@@ -163,7 +163,7 @@ fn headline<'b, 'g, 'r, 's>(
|
||||
|
||||
let (remaining, maybe_todo_keyword) = opt(tuple((
|
||||
space1,
|
||||
parser_with_context!(heading_keyword)(&parser_context),
|
||||
bind_context!(heading_keyword, &parser_context),
|
||||
peek(org_space_or_line_ending),
|
||||
)))(remaining)?;
|
||||
|
||||
@@ -177,9 +177,7 @@ fn headline<'b, 'g, 'r, 's>(
|
||||
|
||||
let (remaining, maybe_title) = opt(tuple((
|
||||
space1,
|
||||
consumed(many1(parser_with_context!(standard_set_object)(
|
||||
&parser_context,
|
||||
))),
|
||||
consumed(many1(bind_context!(standard_set_object, &parser_context))),
|
||||
)))(remaining)?;
|
||||
|
||||
let (remaining, maybe_tags) = opt(tuple((space0, tags)))(remaining)?;
|
||||
@@ -260,7 +258,7 @@ fn heading_keyword<'b, 'g, 'r, 's>(
|
||||
.iter()
|
||||
.map(String::as_str)
|
||||
{
|
||||
let result = tag::<_, _, CustomError<_>>(todo_keyword)(input);
|
||||
let result = tag::<_, _, CustomError>(todo_keyword)(input);
|
||||
if let Ok((remaining, ent)) = result {
|
||||
return Ok((remaining, (TodoKeywordType::Todo, ent)));
|
||||
}
|
||||
@@ -270,14 +268,12 @@ fn heading_keyword<'b, 'g, 'r, 's>(
|
||||
.iter()
|
||||
.map(String::as_str)
|
||||
{
|
||||
let result = tag::<_, _, CustomError<_>>(todo_keyword)(input);
|
||||
let result = tag::<_, _, CustomError>(todo_keyword)(input);
|
||||
if let Ok((remaining, ent)) = result {
|
||||
return Ok((remaining, (TodoKeywordType::Done, ent)));
|
||||
}
|
||||
}
|
||||
Err(nom::Err::Error(CustomError::MyError(MyError(
|
||||
"NoTodoKeyword",
|
||||
))))
|
||||
Err(nom::Err::Error(CustomError::Static("NoTodoKeyword")))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -288,9 +284,9 @@ fn priority_cookie(input: OrgSource<'_>) -> Res<OrgSource<'_>, PriorityCookie> {
|
||||
tag("]"),
|
||||
))(input)?;
|
||||
let cookie = PriorityCookie::try_from(priority_character).map_err(|_| {
|
||||
nom::Err::Error(CustomError::MyError(MyError(
|
||||
nom::Err::Error(CustomError::Static(
|
||||
"Failed to cast priority cookie to number.",
|
||||
)))
|
||||
))
|
||||
})?;
|
||||
Ok((remaining, cookie))
|
||||
}
|
||||
|
||||
@@ -35,7 +35,7 @@ pub(crate) fn scan_for_in_buffer_settings<'s>(
|
||||
let mut remaining = input;
|
||||
loop {
|
||||
// Skip text until possible in_buffer_setting
|
||||
let start_of_pound = take_until::<_, _, CustomError<_>>("#+")(remaining);
|
||||
let start_of_pound = take_until::<_, _, CustomError>("#+")(remaining);
|
||||
let start_of_pound = if let Ok((start_of_pound, _)) = start_of_pound {
|
||||
start_of_pound
|
||||
} else {
|
||||
@@ -47,7 +47,7 @@ pub(crate) fn scan_for_in_buffer_settings<'s>(
|
||||
let (remain, maybe_kw) = match filtered_keyword(in_buffer_settings_key)(start_of_line) {
|
||||
Ok((remain, kw)) => (remain, Some(kw)),
|
||||
Err(_) => {
|
||||
let end_of_line = take_until::<_, _, CustomError<_>>("\n")(start_of_pound);
|
||||
let end_of_line = take_until::<_, _, CustomError>("\n")(start_of_pound);
|
||||
if let Ok((end_of_line, _)) = end_of_line {
|
||||
(end_of_line, None)
|
||||
} else {
|
||||
@@ -84,11 +84,14 @@ fn in_buffer_settings_key<'s>(input: OrgSource<'s>) -> Res<OrgSource<'s>, OrgSou
|
||||
))(input)
|
||||
}
|
||||
|
||||
#[cfg_attr(feature = "tracing", tracing::instrument(level = "debug"))]
|
||||
#[cfg_attr(
|
||||
feature = "tracing",
|
||||
tracing::instrument(level = "debug", skip(original_settings))
|
||||
)]
|
||||
pub(crate) fn apply_in_buffer_settings<'g, 's, 'sf>(
|
||||
keywords: Vec<Keyword<'sf>>,
|
||||
original_settings: &'g GlobalSettings<'g, 's>,
|
||||
) -> Result<GlobalSettings<'g, 's>, String> {
|
||||
) -> Result<GlobalSettings<'g, 's>, CustomError> {
|
||||
let mut new_settings = original_settings.clone();
|
||||
|
||||
// Todo Keywords
|
||||
@@ -98,7 +101,11 @@ pub(crate) fn apply_in_buffer_settings<'g, 's, 'sf>(
|
||||
|| kw.key.eq_ignore_ascii_case("typ_todo")
|
||||
}) {
|
||||
let (_, (in_progress_words, complete_words)) =
|
||||
todo_keywords(kw.value).map_err(|err| err.to_string())?;
|
||||
todo_keywords(kw.value).map_err(|err| match err {
|
||||
nom::Err::Incomplete(_) => CustomError::Text(err.to_string()),
|
||||
nom::Err::Error(e) => e,
|
||||
nom::Err::Failure(e) => e,
|
||||
})?;
|
||||
new_settings
|
||||
.in_progress_todo_keywords
|
||||
.extend(in_progress_words.into_iter().map(str::to_string));
|
||||
@@ -112,9 +119,14 @@ pub(crate) fn apply_in_buffer_settings<'g, 's, 'sf>(
|
||||
.iter()
|
||||
.filter(|kw| kw.key.eq_ignore_ascii_case("startup"))
|
||||
{
|
||||
let (_remaining, settings) =
|
||||
separated_list0(space1::<&str, nom::error::Error<_>>, is_not(" \t"))(kw.value)
|
||||
.map_err(|err: nom::Err<_>| err.to_string())?;
|
||||
let (_remaining, settings) = separated_list0(space1::<&str, CustomError>, is_not(" \t"))(
|
||||
kw.value,
|
||||
)
|
||||
.map_err(|err: nom::Err<_>| match err {
|
||||
nom::Err::Incomplete(_) => CustomError::Text(err.to_string()),
|
||||
nom::Err::Error(e) => e,
|
||||
nom::Err::Failure(e) => e,
|
||||
})?;
|
||||
if settings.contains(&"odd") {
|
||||
new_settings.odd_levels_only = HeadlineLevelFilter::Odd;
|
||||
}
|
||||
@@ -128,7 +140,11 @@ pub(crate) fn apply_in_buffer_settings<'g, 's, 'sf>(
|
||||
.iter()
|
||||
.filter(|kw| kw.key.eq_ignore_ascii_case("link"))
|
||||
{
|
||||
let (_, (link_key, link_value)) = link_template(kw.value).map_err(|e| e.to_string())?;
|
||||
let (_, (link_key, link_value)) = link_template(kw.value).map_err(|err| match err {
|
||||
nom::Err::Incomplete(_) => CustomError::Text(err.to_string()),
|
||||
nom::Err::Error(e) => e,
|
||||
nom::Err::Failure(e) => e,
|
||||
})?;
|
||||
new_settings
|
||||
.link_templates
|
||||
.insert(link_key.to_owned(), link_value.to_owned());
|
||||
@@ -139,9 +155,7 @@ pub(crate) fn apply_in_buffer_settings<'g, 's, 'sf>(
|
||||
|
||||
/// Apply in-buffer settings that do not impact parsing and therefore can be applied after parsing.
|
||||
#[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>,
|
||||
) -> Result<(), &'static str> {
|
||||
pub(crate) fn apply_post_parse_in_buffer_settings<'g, 's, 'sf>(document: &mut Document<'s>) {
|
||||
document.category = Into::<AstNode>::into(&*document)
|
||||
.into_iter()
|
||||
.filter_map(|ast_node| {
|
||||
@@ -154,7 +168,6 @@ pub(crate) fn apply_post_parse_in_buffer_settings<'g, 's, 'sf>(
|
||||
})
|
||||
.last()
|
||||
.map(|kw| kw.value.to_owned());
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
|
||||
|
||||
@@ -19,7 +19,6 @@ use crate::context::ExitClass;
|
||||
use crate::context::ExitMatcherNode;
|
||||
use crate::context::RefContext;
|
||||
use crate::error::CustomError;
|
||||
use crate::error::MyError;
|
||||
use crate::error::Res;
|
||||
use crate::parser::util::exit_matcher_parser;
|
||||
use crate::parser::util::get_consumed;
|
||||
@@ -131,9 +130,7 @@ fn _header_end<'b, 'g, 'r, 's>(
|
||||
let current_depth = input.get_bracket_depth() - starting_bracket_depth;
|
||||
if current_depth > 0 {
|
||||
// Its impossible for the next character to end the header if we're any amount of bracket deep
|
||||
return Err(nom::Err::Error(CustomError::MyError(MyError(
|
||||
"NoHeaderEnd",
|
||||
))));
|
||||
return Err(nom::Err::Error(CustomError::Static("NoHeaderEnd")));
|
||||
}
|
||||
if current_depth < 0 {
|
||||
// This shouldn't be possible because if depth is 0 then a closing bracket should end the header.
|
||||
@@ -183,9 +180,7 @@ fn _argument_end<'b, 'g, 'r, 's>(
|
||||
let current_depth = input.get_parenthesis_depth() - starting_parenthesis_depth;
|
||||
if current_depth > 0 {
|
||||
// Its impossible for the next character to end the argument if we're any amount of parenthesis deep
|
||||
return Err(nom::Err::Error(CustomError::MyError(MyError(
|
||||
"NoArgumentEnd",
|
||||
))));
|
||||
return Err(nom::Err::Error(CustomError::Static("NoArgumentEnd")));
|
||||
}
|
||||
if current_depth < 0 {
|
||||
// This shouldn't be possible because if depth is 0 then a closing parenthesis should end the argument.
|
||||
|
||||
@@ -21,7 +21,6 @@ use crate::context::ExitClass;
|
||||
use crate::context::ExitMatcherNode;
|
||||
use crate::context::RefContext;
|
||||
use crate::error::CustomError;
|
||||
use crate::error::MyError;
|
||||
use crate::error::Res;
|
||||
use crate::parser::util::exit_matcher_parser;
|
||||
use crate::parser::util::get_consumed;
|
||||
@@ -125,9 +124,7 @@ fn _header_end<'b, 'g, 'r, 's>(
|
||||
let current_depth = input.get_bracket_depth() - starting_bracket_depth;
|
||||
if current_depth > 0 {
|
||||
// Its impossible for the next character to end the header if we're any amount of bracket deep
|
||||
return Err(nom::Err::Error(CustomError::MyError(MyError(
|
||||
"NoHeaderEnd",
|
||||
))));
|
||||
return Err(nom::Err::Error(CustomError::Static("NoHeaderEnd")));
|
||||
}
|
||||
if current_depth < 0 {
|
||||
// This shouldn't be possible because if depth is 0 then a closing bracket should end the header.
|
||||
@@ -187,7 +184,7 @@ fn _body_end<'b, 'g, 'r, 's>(
|
||||
let current_depth = input.get_brace_depth() - starting_brace_depth;
|
||||
if current_depth > 0 {
|
||||
// Its impossible for the next character to end the body if we're any amount of brace deep
|
||||
return Err(nom::Err::Error(CustomError::MyError(MyError("NoBodyEnd"))));
|
||||
return Err(nom::Err::Error(CustomError::Static("NoBodyEnd")));
|
||||
}
|
||||
if current_depth < 0 {
|
||||
// This shouldn't be possible because if depth is 0 then a closing brace should end the body.
|
||||
|
||||
@@ -4,11 +4,9 @@ use nom::bytes::complete::tag;
|
||||
use nom::bytes::complete::tag_no_case;
|
||||
use nom::bytes::complete::take_while1;
|
||||
use nom::character::complete::anychar;
|
||||
use nom::character::complete::line_ending;
|
||||
use nom::character::complete::one_of;
|
||||
use nom::character::complete::space0;
|
||||
use nom::combinator::consumed;
|
||||
use nom::combinator::eof;
|
||||
use nom::combinator::map;
|
||||
use nom::combinator::not;
|
||||
use nom::combinator::peek;
|
||||
@@ -22,10 +20,11 @@ use super::org_source::BracketDepth;
|
||||
use super::org_source::OrgSource;
|
||||
use super::util::get_consumed;
|
||||
use super::util::maybe_consume_trailing_whitespace_if_not_exiting;
|
||||
use crate::context::parser_with_context;
|
||||
use super::util::org_line_ending;
|
||||
use crate::context::constants::ORG_ELEMENT_AFFILIATED_KEYWORDS;
|
||||
use crate::context::constants::ORG_ELEMENT_DUAL_KEYWORDS;
|
||||
use crate::context::RefContext;
|
||||
use crate::error::CustomError;
|
||||
use crate::error::MyError;
|
||||
use crate::error::Res;
|
||||
use crate::parser::macros::element;
|
||||
use crate::parser::util::start_of_line;
|
||||
@@ -50,11 +49,8 @@ fn _filtered_keyword<'s, F: Fn(OrgSource<'s>) -> Res<OrgSource<'s>, OrgSource<'s
|
||||
// TODO: When key is a member of org-element-parsed-keywords, value can contain the standard set objects, excluding footnote references.
|
||||
let (remaining, (consumed_input, (_, _, parsed_key, _))) =
|
||||
consumed(tuple((space0, tag("#+"), key_parser, tag(":"))))(input)?;
|
||||
if let Ok((remaining, _)) = tuple((
|
||||
space0::<OrgSource<'_>, CustomError<OrgSource<'_>>>,
|
||||
alt((line_ending, eof)),
|
||||
))(remaining)
|
||||
{
|
||||
let (remaining, _ws) = space0(remaining)?;
|
||||
if let Ok((remaining, _)) = org_line_ending(remaining) {
|
||||
return Ok((
|
||||
remaining,
|
||||
Keyword {
|
||||
@@ -65,12 +61,9 @@ fn _filtered_keyword<'s, F: Fn(OrgSource<'s>) -> Res<OrgSource<'s>, OrgSource<'s
|
||||
},
|
||||
));
|
||||
}
|
||||
let (remaining, _ws) = space0(remaining)?;
|
||||
let (remaining, parsed_value) = recognize(many_till(
|
||||
anychar,
|
||||
peek(tuple((space0, alt((line_ending, eof))))),
|
||||
))(remaining)?;
|
||||
let (remaining, _ws) = tuple((space0, alt((line_ending, eof))))(remaining)?;
|
||||
let (remaining, parsed_value) =
|
||||
recognize(many_till(anychar, peek(tuple((space0, org_line_ending)))))(remaining)?;
|
||||
let (remaining, _ws) = tuple((space0, org_line_ending))(remaining)?;
|
||||
Ok((
|
||||
remaining,
|
||||
Keyword {
|
||||
@@ -106,11 +99,8 @@ where
|
||||
}
|
||||
|
||||
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
|
||||
pub(crate) fn affiliated_keyword<'b, 'g, 'r, 's>(
|
||||
context: RefContext<'b, 'g, 'r, 's>,
|
||||
input: OrgSource<'s>,
|
||||
) -> Res<OrgSource<'s>, Keyword<'s>> {
|
||||
filtered_keyword(parser_with_context!(affiliated_key)(context))(input)
|
||||
pub(crate) fn affiliated_keyword<'s>(input: OrgSource<'s>) -> Res<OrgSource<'s>, Keyword<'s>> {
|
||||
filtered_keyword(affiliated_key)(input)
|
||||
}
|
||||
|
||||
#[cfg_attr(
|
||||
@@ -145,29 +135,18 @@ fn regular_keyword_key<'s>(input: OrgSource<'s>) -> Res<OrgSource<'s>, OrgSource
|
||||
}
|
||||
|
||||
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
|
||||
fn affiliated_key<'b, 'g, 'r, 's>(
|
||||
context: RefContext<'b, 'g, 'r, 's>,
|
||||
input: OrgSource<'s>,
|
||||
) -> Res<OrgSource<'s>, OrgSource<'s>> {
|
||||
element!(dual_affiliated_key, context, input);
|
||||
element!(plain_affiliated_key, context, input);
|
||||
fn affiliated_key<'s>(input: OrgSource<'s>) -> Res<OrgSource<'s>, OrgSource<'s>> {
|
||||
element!(dual_affiliated_key, input);
|
||||
element!(plain_affiliated_key, input);
|
||||
element!(export_keyword, input);
|
||||
Err(nom::Err::Error(CustomError::MyError(MyError(
|
||||
"No affiliated key.",
|
||||
))))
|
||||
Err(nom::Err::Error(CustomError::Static("No affiliated key.")))
|
||||
}
|
||||
|
||||
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
|
||||
fn plain_affiliated_key<'b, 'g, 'r, 's>(
|
||||
context: RefContext<'b, 'g, 'r, 's>,
|
||||
input: OrgSource<'s>,
|
||||
) -> Res<OrgSource<'s>, OrgSource<'s>> {
|
||||
for keyword in context.get_global_settings().element_affiliated_keywords {
|
||||
fn plain_affiliated_key<'s>(input: OrgSource<'s>) -> Res<OrgSource<'s>, OrgSource<'s>> {
|
||||
for keyword in ORG_ELEMENT_AFFILIATED_KEYWORDS {
|
||||
let result = map(
|
||||
tuple((
|
||||
tag_no_case::<_, _, CustomError<_>>(*keyword),
|
||||
peek(tag(":")),
|
||||
)),
|
||||
tuple((tag_no_case::<_, _, CustomError>(keyword), peek(tag(":")))),
|
||||
|(key, _)| key,
|
||||
)(input);
|
||||
if let Ok((remaining, ent)) = result {
|
||||
@@ -175,19 +154,14 @@ fn plain_affiliated_key<'b, 'g, 'r, 's>(
|
||||
}
|
||||
}
|
||||
|
||||
Err(nom::Err::Error(CustomError::MyError(MyError(
|
||||
"NoKeywordKey",
|
||||
))))
|
||||
Err(nom::Err::Error(CustomError::Static("NoKeywordKey")))
|
||||
}
|
||||
|
||||
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
|
||||
fn dual_affiliated_key<'b, 'g, 'r, 's>(
|
||||
context: RefContext<'b, 'g, 'r, 's>,
|
||||
input: OrgSource<'s>,
|
||||
) -> Res<OrgSource<'s>, OrgSource<'s>> {
|
||||
for keyword in context.get_global_settings().element_dual_keywords {
|
||||
fn dual_affiliated_key<'s>(input: OrgSource<'s>) -> Res<OrgSource<'s>, OrgSource<'s>> {
|
||||
for keyword in ORG_ELEMENT_DUAL_KEYWORDS {
|
||||
let result = recognize(tuple((
|
||||
tag_no_case::<_, _, CustomError<_>>(*keyword),
|
||||
tag_no_case::<_, _, CustomError>(keyword),
|
||||
tag("["),
|
||||
optval,
|
||||
tag("]"),
|
||||
@@ -198,9 +172,7 @@ fn dual_affiliated_key<'b, 'g, 'r, 's>(
|
||||
}
|
||||
}
|
||||
|
||||
Err(nom::Err::Error(CustomError::MyError(MyError(
|
||||
"NoKeywordKey",
|
||||
))))
|
||||
Err(nom::Err::Error(CustomError::Static("NoKeywordKey")))
|
||||
}
|
||||
|
||||
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
|
||||
@@ -228,7 +200,7 @@ fn _optval_end<'s>(
|
||||
unreachable!("Exceeded optval bracket depth.")
|
||||
}
|
||||
if current_depth == 0 {
|
||||
let close_bracket = tag::<_, _, CustomError<_>>("]")(input);
|
||||
let close_bracket = tag::<_, _, CustomError>("]")(input);
|
||||
if close_bracket.is_ok() {
|
||||
return close_bracket;
|
||||
}
|
||||
@@ -249,19 +221,12 @@ mod tests {
|
||||
use test::Bencher;
|
||||
|
||||
use super::*;
|
||||
use crate::context::Context;
|
||||
use crate::context::ContextElement;
|
||||
use crate::context::GlobalSettings;
|
||||
use crate::context::List;
|
||||
use crate::parser::OrgSource;
|
||||
|
||||
#[bench]
|
||||
fn bench_affiliated_keyword(b: &mut Bencher) {
|
||||
let input = OrgSource::new("#+CAPTION[*foo*]: bar *baz*");
|
||||
let global_settings = GlobalSettings::default();
|
||||
let initial_context = ContextElement::document_context();
|
||||
let initial_context = Context::new(&global_settings, List::new(&initial_context));
|
||||
|
||||
b.iter(|| assert!(affiliated_keyword(&initial_context, input).is_ok()));
|
||||
b.iter(|| assert!(affiliated_keyword(input).is_ok()));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,7 +17,6 @@ use super::util::maybe_consume_object_trailing_whitespace_if_not_exiting;
|
||||
use crate::context::parser_with_context;
|
||||
use crate::context::RefContext;
|
||||
use crate::error::CustomError;
|
||||
use crate::error::MyError;
|
||||
use crate::error::Res;
|
||||
use crate::parser::util::exit_matcher_parser;
|
||||
use crate::parser::util::get_consumed;
|
||||
@@ -209,9 +208,9 @@ fn pre<'b, 'g, 'r, 's>(
|
||||
) -> Res<OrgSource<'s>, ()> {
|
||||
let preceding_character = input.get_preceding_character();
|
||||
if let Some('$') = preceding_character {
|
||||
return Err(nom::Err::Error(CustomError::MyError(MyError(
|
||||
return Err(nom::Err::Error(CustomError::Static(
|
||||
"Not a valid pre character for dollar char fragment.",
|
||||
))));
|
||||
)));
|
||||
}
|
||||
Ok((input, ()))
|
||||
}
|
||||
@@ -283,9 +282,9 @@ fn close_border<'b, 'g, 'r, 's>(
|
||||
match preceding_character {
|
||||
Some(c) if !c.is_whitespace() && !".,;$".contains(c) => Ok((input, ())),
|
||||
_ => {
|
||||
return Err(nom::Err::Error(CustomError::MyError(MyError(
|
||||
return Err(nom::Err::Error(CustomError::Static(
|
||||
"Not a valid pre character for dollar char fragment.",
|
||||
))));
|
||||
)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -27,7 +27,6 @@ use crate::context::ExitClass;
|
||||
use crate::context::ExitMatcherNode;
|
||||
use crate::context::RefContext;
|
||||
use crate::error::CustomError;
|
||||
use crate::error::MyError;
|
||||
use crate::error::Res;
|
||||
use crate::parser::object_parser::standard_set_object;
|
||||
use crate::parser::util::blank_line;
|
||||
@@ -604,7 +603,7 @@ fn _example_src_switches<'s>(
|
||||
}
|
||||
}
|
||||
if !matched_a_word {
|
||||
return Err(nom::Err::Error(CustomError::MyError(MyError("No words."))));
|
||||
return Err(nom::Err::Error(CustomError::Static("No words.")));
|
||||
}
|
||||
let remaining = last_match_remaining;
|
||||
|
||||
|
||||
@@ -7,7 +7,6 @@ use nom::multi::many0;
|
||||
use super::org_source::OrgSource;
|
||||
use crate::context::RefContext;
|
||||
use crate::error::CustomError;
|
||||
use crate::error::MyError;
|
||||
use crate::error::Res;
|
||||
use crate::parser::util::get_consumed;
|
||||
use crate::types::LineBreak;
|
||||
@@ -45,9 +44,9 @@ fn pre<'b, 'g, 'r, 's>(
|
||||
match preceding_character {
|
||||
// If None, we are at the start of the file
|
||||
None | Some('\\') => {
|
||||
return Err(nom::Err::Error(CustomError::MyError(MyError(
|
||||
return Err(nom::Err::Error(CustomError::Static(
|
||||
"Not a valid pre character for line break.",
|
||||
))));
|
||||
)));
|
||||
}
|
||||
_ => {}
|
||||
};
|
||||
@@ -55,9 +54,9 @@ fn pre<'b, 'g, 'r, 's>(
|
||||
let current_line = input.text_since_line_break();
|
||||
let is_non_empty_line = current_line.chars().any(|c| !c.is_whitespace());
|
||||
if !is_non_empty_line {
|
||||
return Err(nom::Err::Error(CustomError::MyError(MyError(
|
||||
return Err(nom::Err::Error(CustomError::Static(
|
||||
"Not a valid pre line for line break.",
|
||||
))));
|
||||
)));
|
||||
}
|
||||
|
||||
Ok((input, ()))
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
mod affiliated_keyword;
|
||||
mod angle_link;
|
||||
mod babel_call;
|
||||
mod bullshitium;
|
||||
mod citation;
|
||||
mod citation_reference;
|
||||
mod clock;
|
||||
|
||||
@@ -4,7 +4,6 @@ use super::regular_link::regular_link;
|
||||
use super::subscript_and_superscript::detect_subscript_or_superscript;
|
||||
use crate::context::RefContext;
|
||||
use crate::error::CustomError;
|
||||
use crate::error::MyError;
|
||||
use crate::error::Res;
|
||||
use crate::parser::angle_link::angle_link;
|
||||
use crate::parser::citation::citation;
|
||||
@@ -43,7 +42,7 @@ pub(crate) fn standard_set_object<'b, 'g, 'r, 's>(
|
||||
input,
|
||||
Object::PlainText
|
||||
);
|
||||
Err(nom::Err::Error(CustomError::MyError(MyError("No object."))))
|
||||
Err(nom::Err::Error(CustomError::Static("No object.")))
|
||||
}
|
||||
|
||||
#[cfg_attr(
|
||||
@@ -61,7 +60,7 @@ pub(crate) fn minimal_set_object<'b, 'g, 'r, 's>(
|
||||
input,
|
||||
Object::PlainText
|
||||
);
|
||||
Err(nom::Err::Error(CustomError::MyError(MyError("No object."))))
|
||||
Err(nom::Err::Error(CustomError::Static("No object.")))
|
||||
}
|
||||
|
||||
#[cfg_attr(
|
||||
@@ -103,7 +102,7 @@ fn standard_set_object_sans_plain_text<'b, 'g, 'r, 's>(
|
||||
element!(angle_link, context, input, Object::AngleLink);
|
||||
element!(org_macro, context, input, Object::OrgMacro);
|
||||
|
||||
Err(nom::Err::Error(CustomError::MyError(MyError("No object."))))
|
||||
Err(nom::Err::Error(CustomError::Static("No object.")))
|
||||
}
|
||||
|
||||
#[cfg_attr(
|
||||
@@ -119,7 +118,7 @@ fn minimal_set_object_sans_plain_text<'b, 'g, 'r, 's>(
|
||||
element!(entity, context, input, Object::Entity);
|
||||
element!(latex_fragment, context, input, Object::LatexFragment);
|
||||
element!(text_markup, context, input);
|
||||
Err(nom::Err::Error(CustomError::MyError(MyError("No object."))))
|
||||
Err(nom::Err::Error(CustomError::Static("No object.")))
|
||||
}
|
||||
|
||||
#[cfg_attr(
|
||||
@@ -137,9 +136,7 @@ pub(crate) fn detect_standard_set_object_sans_plain_text<'b, 'g, 'r, 's>(
|
||||
return Ok((input, ()));
|
||||
}
|
||||
|
||||
return Err(nom::Err::Error(CustomError::MyError(MyError(
|
||||
"No object detected.",
|
||||
))));
|
||||
return Err(nom::Err::Error(CustomError::Static("No object detected.")));
|
||||
}
|
||||
|
||||
#[cfg_attr(
|
||||
@@ -157,9 +154,7 @@ fn detect_minimal_set_object_sans_plain_text<'b, 'g, 'r, 's>(
|
||||
return Ok((input, ()));
|
||||
}
|
||||
|
||||
return Err(nom::Err::Error(CustomError::MyError(MyError(
|
||||
"No object detected.",
|
||||
))));
|
||||
return Err(nom::Err::Error(CustomError::Static("No object detected.")));
|
||||
}
|
||||
|
||||
#[cfg_attr(
|
||||
@@ -182,7 +177,7 @@ pub(crate) fn regular_link_description_set_object<'b, 'g, 'r, 's>(
|
||||
input,
|
||||
Object::PlainText
|
||||
);
|
||||
Err(nom::Err::Error(CustomError::MyError(MyError("No object."))))
|
||||
Err(nom::Err::Error(CustomError::Static("No object.")))
|
||||
}
|
||||
|
||||
#[cfg_attr(
|
||||
@@ -205,7 +200,7 @@ fn regular_link_description_set_object_sans_plain_text<'b, 'g, 'r, 's>(
|
||||
element!(inline_babel_call, context, input, Object::InlineBabelCall);
|
||||
element!(org_macro, context, input, Object::OrgMacro);
|
||||
element!(minimal_set_object_sans_plain_text, context, input);
|
||||
Err(nom::Err::Error(CustomError::MyError(MyError("No object."))))
|
||||
Err(nom::Err::Error(CustomError::Static("No object.")))
|
||||
}
|
||||
|
||||
#[cfg_attr(
|
||||
@@ -223,9 +218,7 @@ fn detect_regular_link_description_set_object_sans_plain_text<'b, 'g, 'r, 's>(
|
||||
return Ok((input, ()));
|
||||
}
|
||||
|
||||
Err(nom::Err::Error(CustomError::MyError(MyError(
|
||||
"No object detected.",
|
||||
))))
|
||||
Err(nom::Err::Error(CustomError::Static("No object detected.")))
|
||||
}
|
||||
|
||||
#[cfg_attr(
|
||||
@@ -243,7 +236,7 @@ pub(crate) fn table_cell_set_object<'b, 'g, 'r, 's>(
|
||||
input,
|
||||
Object::PlainText
|
||||
);
|
||||
Err(nom::Err::Error(CustomError::MyError(MyError("No object."))))
|
||||
Err(nom::Err::Error(CustomError::Static("No object.")))
|
||||
}
|
||||
|
||||
#[cfg_attr(
|
||||
@@ -271,7 +264,7 @@ fn table_cell_set_object_sans_plain_text<'b, 'g, 'r, 's>(
|
||||
element!(target, context, input, Object::Target);
|
||||
element!(timestamp, context, input, Object::Timestamp);
|
||||
element!(minimal_set_object_sans_plain_text, context, input);
|
||||
Err(nom::Err::Error(CustomError::MyError(MyError("No object."))))
|
||||
Err(nom::Err::Error(CustomError::Static("No object.")))
|
||||
}
|
||||
|
||||
#[cfg_attr(
|
||||
@@ -289,7 +282,5 @@ fn detect_table_cell_set_object_sans_plain_text<'b, 'g, 'r, 's>(
|
||||
return Ok((input, ()));
|
||||
}
|
||||
|
||||
return Err(nom::Err::Error(CustomError::MyError(MyError(
|
||||
"No object detected.",
|
||||
))));
|
||||
return Err(nom::Err::Error(CustomError::Static("No object detected.")));
|
||||
}
|
||||
|
||||
@@ -96,7 +96,7 @@ fn org_macro_arg<'b, 'g, 'r, 's>(
|
||||
loop {
|
||||
not(parser_with_context!(exit_matcher_parser)(context))(remaining)?;
|
||||
not(peek(tag("}}}")))(remaining)?;
|
||||
if peek(tuple((space0::<OrgSource<'_>, CustomError<_>>, tag(")"))))(remaining).is_ok() {
|
||||
if peek(tuple((space0::<_, CustomError>, tag(")"))))(remaining).is_ok() {
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -108,7 +108,7 @@ fn org_macro_arg<'b, 'g, 'r, 's>(
|
||||
}
|
||||
if next_char == '\\' {
|
||||
escaping = true;
|
||||
if peek(tag::<_, _, CustomError<_>>(")"))(new_remaining).is_ok() {
|
||||
if peek(tag::<_, _, CustomError>(")"))(new_remaining).is_ok() {
|
||||
// Special case for backslash at the end of a macro
|
||||
remaining = new_remaining;
|
||||
break;
|
||||
|
||||
@@ -10,9 +10,6 @@ use nom::InputTakeAtPosition;
|
||||
use nom::Offset;
|
||||
use nom::Slice;
|
||||
|
||||
use crate::error::CustomError;
|
||||
use crate::error::MyError;
|
||||
|
||||
pub(crate) type BracketDepth = i16;
|
||||
|
||||
#[derive(Copy, Clone, PartialEq)]
|
||||
@@ -85,6 +82,15 @@ impl<'s> OrgSource<'s> {
|
||||
self.slice(..(other.end - self.start))
|
||||
}
|
||||
|
||||
pub(crate) fn get_until_end_of_str(&self, other: &'s str) -> OrgSource<'s> {
|
||||
let full_source_start = self.full_source.as_ptr() as usize;
|
||||
let other_start = other.as_ptr() as usize - full_source_start;
|
||||
let other_end = other_start + other.len();
|
||||
debug_assert!(other_start >= self.start);
|
||||
debug_assert!(other_end <= self.end);
|
||||
self.slice(..(other_end - self.start))
|
||||
}
|
||||
|
||||
pub(crate) fn get_start_of_line(&self) -> OrgSource<'s> {
|
||||
let skipped_text = self.text_since_line_break();
|
||||
let mut bracket_depth = self.bracket_depth;
|
||||
@@ -385,33 +391,6 @@ impl<'n, 's> FindSubstring<&'n str> for OrgSource<'s> {
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn convert_error<'a, I: Into<CustomError<&'a str>>>(
|
||||
err: nom::Err<I>,
|
||||
) -> nom::Err<CustomError<&'a str>> {
|
||||
match err {
|
||||
nom::Err::Incomplete(needed) => nom::Err::Incomplete(needed),
|
||||
nom::Err::Error(err) => nom::Err::Error(err.into()),
|
||||
nom::Err::Failure(err) => nom::Err::Failure(err.into()),
|
||||
}
|
||||
}
|
||||
|
||||
impl<'s> From<CustomError<OrgSource<'s>>> for CustomError<&'s str> {
|
||||
fn from(value: CustomError<OrgSource<'s>>) -> Self {
|
||||
match value {
|
||||
CustomError::MyError(err) => CustomError::MyError(err),
|
||||
CustomError::Nom(input, error_kind) => CustomError::Nom(input.into(), error_kind),
|
||||
CustomError::IO(err) => CustomError::IO(err),
|
||||
CustomError::BoxedError(err) => CustomError::BoxedError(err),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'s> From<MyError<OrgSource<'s>>> for MyError<&'s str> {
|
||||
fn from(value: MyError<OrgSource<'s>>) -> Self {
|
||||
MyError(value.0.into())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
@@ -36,7 +36,6 @@ use crate::context::ExitMatcherNode;
|
||||
use crate::context::Matcher;
|
||||
use crate::context::RefContext;
|
||||
use crate::error::CustomError;
|
||||
use crate::error::MyError;
|
||||
use crate::error::Res;
|
||||
use crate::parser::util::exit_matcher_parser;
|
||||
use crate::parser::util::get_consumed;
|
||||
@@ -95,9 +94,9 @@ fn pre<'b, 'g, 'r, 's>(
|
||||
Some(x) if !WORD_CONSTITUENT_CHARACTERS.contains(x) => {}
|
||||
Some(_) => {
|
||||
// Not at start of line, cannot be a heading
|
||||
return Err(nom::Err::Error(CustomError::MyError(MyError(
|
||||
return Err(nom::Err::Error(CustomError::Static(
|
||||
"Not a valid pre character for plain link.",
|
||||
))));
|
||||
)));
|
||||
}
|
||||
};
|
||||
Ok((input, ()))
|
||||
@@ -262,15 +261,13 @@ pub(crate) fn protocol<'b, 'g, 'r, 's>(
|
||||
input: OrgSource<'s>,
|
||||
) -> Res<OrgSource<'s>, OrgSource<'s>> {
|
||||
for link_parameter in context.get_global_settings().link_parameters {
|
||||
let result = tag_no_case::<_, _, CustomError<_>>(*link_parameter)(input);
|
||||
let result = tag_no_case::<_, _, CustomError>(*link_parameter)(input);
|
||||
if let Ok((remaining, ent)) = result {
|
||||
return Ok((remaining, ent));
|
||||
}
|
||||
}
|
||||
|
||||
Err(nom::Err::Error(CustomError::MyError(MyError(
|
||||
"NoLinkProtocol",
|
||||
))))
|
||||
Err(nom::Err::Error(CustomError::Static("NoLinkProtocol")))
|
||||
}
|
||||
|
||||
#[cfg_attr(
|
||||
@@ -327,8 +324,7 @@ fn impl_path_plain_end<'b, 'g, 'r, 's>(
|
||||
}
|
||||
|
||||
if current_depth == 0 {
|
||||
let close_parenthesis =
|
||||
tag::<&str, OrgSource<'_>, CustomError<OrgSource<'_>>>(")")(remaining);
|
||||
let close_parenthesis = tag::<_, _, CustomError>(")")(remaining);
|
||||
if close_parenthesis.is_ok() {
|
||||
return close_parenthesis;
|
||||
}
|
||||
@@ -342,9 +338,7 @@ fn impl_path_plain_end<'b, 'g, 'r, 's>(
|
||||
}
|
||||
}
|
||||
|
||||
Err(nom::Err::Error(CustomError::MyError(MyError(
|
||||
"No path plain end",
|
||||
))))
|
||||
Err(nom::Err::Error(CustomError::Static("No path plain end")))
|
||||
}
|
||||
|
||||
#[cfg_attr(
|
||||
@@ -420,18 +414,18 @@ fn _path_plain_parenthesis_end<'s>(
|
||||
unreachable!("Exceeded plain link parenthesis depth.")
|
||||
}
|
||||
if current_depth == 0 {
|
||||
let close_parenthesis = tag::<&str, OrgSource<'_>, CustomError<OrgSource<'_>>>(")")(input);
|
||||
let close_parenthesis = tag::<_, _, CustomError>(")")(input);
|
||||
if close_parenthesis.is_ok() {
|
||||
return close_parenthesis;
|
||||
}
|
||||
}
|
||||
if current_depth == 1 {
|
||||
let open_parenthesis = tag::<&str, OrgSource<'_>, CustomError<OrgSource<'_>>>("(")(input);
|
||||
let open_parenthesis = tag::<_, _, CustomError>("(")(input);
|
||||
if open_parenthesis.is_ok() {
|
||||
return open_parenthesis;
|
||||
}
|
||||
}
|
||||
Err(nom::Err::Error(CustomError::MyError(MyError(
|
||||
Err(nom::Err::Error(CustomError::Static(
|
||||
"No closing parenthesis",
|
||||
))))
|
||||
)))
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ use nom::bytes::complete::tag;
|
||||
use nom::character::complete::anychar;
|
||||
use nom::character::complete::digit1;
|
||||
use nom::character::complete::line_ending;
|
||||
use nom::character::complete::multispace1;
|
||||
use nom::character::complete::one_of;
|
||||
use nom::character::complete::space0;
|
||||
use nom::character::complete::space1;
|
||||
@@ -17,6 +18,7 @@ use nom::multi::many0;
|
||||
use nom::multi::many1;
|
||||
use nom::multi::many_till;
|
||||
use nom::sequence::tuple;
|
||||
use nom::InputTake;
|
||||
|
||||
use super::affiliated_keyword::parse_affiliated_keywords;
|
||||
use super::element_parser::element;
|
||||
@@ -25,6 +27,7 @@ use super::org_source::OrgSource;
|
||||
use super::util::include_input;
|
||||
use super::util::indentation_level;
|
||||
use super::util::non_whitespace_character;
|
||||
use crate::context::bind_context;
|
||||
use crate::context::parser_with_context;
|
||||
use crate::context::ContextElement;
|
||||
use crate::context::ContextMatcher;
|
||||
@@ -32,7 +35,6 @@ use crate::context::ExitClass;
|
||||
use crate::context::ExitMatcherNode;
|
||||
use crate::context::RefContext;
|
||||
use crate::error::CustomError;
|
||||
use crate::error::MyError;
|
||||
use crate::error::Res;
|
||||
use crate::parser::util::blank_line;
|
||||
use crate::parser::util::exit_matcher_parser;
|
||||
@@ -78,9 +80,47 @@ where
|
||||
{
|
||||
return Ok((input, ()));
|
||||
}
|
||||
return Err(nom::Err::Error(CustomError::MyError(MyError(
|
||||
"No element detected.",
|
||||
))));
|
||||
return Err(nom::Err::Error(CustomError::Static("No element detected.")));
|
||||
}
|
||||
|
||||
#[cfg_attr(
|
||||
feature = "tracing",
|
||||
tracing::instrument(ret, level = "debug", skip(context))
|
||||
)]
|
||||
pub(crate) fn detect_not_plain_list_item_indent<'b, 'g, 'r, 's>(
|
||||
context: RefContext<'b, 'g, 'r, 's>,
|
||||
input: OrgSource<'s>,
|
||||
) -> Res<OrgSource<'s>, (u16, OrgSource<'s>)> {
|
||||
if let Ok((_remaining, (_, indent, _))) = tuple((
|
||||
start_of_line,
|
||||
parser_with_context!(indentation_level)(context),
|
||||
not(tuple((
|
||||
parser_with_context!(bullet)(context),
|
||||
alt((space1, line_ending, eof)),
|
||||
))),
|
||||
))(input)
|
||||
{
|
||||
return Ok((input, indent));
|
||||
}
|
||||
|
||||
// Headlines are not plain list items.
|
||||
if let Ok((_remaining, (_, indent, _))) = verify(
|
||||
tuple((
|
||||
start_of_line,
|
||||
parser_with_context!(indentation_level)(context),
|
||||
tuple((
|
||||
parser_with_context!(bullet)(context),
|
||||
alt((space1, line_ending, eof)),
|
||||
)),
|
||||
)),
|
||||
|(_, (depth, _), ((_, bullet), _))| {
|
||||
*depth == 0 && Into::<&str>::into(bullet).starts_with('*')
|
||||
},
|
||||
)(input)
|
||||
{
|
||||
return Ok((input, indent));
|
||||
}
|
||||
return Err(nom::Err::Error(CustomError::Static("No element detected.")));
|
||||
}
|
||||
|
||||
#[cfg_attr(
|
||||
@@ -123,7 +163,7 @@ where
|
||||
// While #3 is the most slow, it also seems to cleanest and involves the least manual mutation of already-parsed objects so I am going with #3 for now, but we should revisit #1 or #2 when the parser is more developed.
|
||||
|
||||
loop {
|
||||
let list_item = parser_with_context!(plain_list_item)(&parser_context)(remaining);
|
||||
let list_item = plain_list_item(&parser_context, remaining);
|
||||
match (&first_item_list_type, &list_item) {
|
||||
(None, Ok((_remain, (list_type, _item)))) => {
|
||||
let _ = first_item_list_type.insert(*list_type);
|
||||
@@ -143,25 +183,17 @@ where
|
||||
}
|
||||
};
|
||||
|
||||
let maybe_exit = parser_with_context!(exit_matcher_parser)(&parser_context)(remaining);
|
||||
let maybe_exit = exit_matcher_parser(&parser_context, remaining);
|
||||
if maybe_exit.is_ok() {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
let (final_child_start, _final_item_first_parse) = match children.pop() {
|
||||
Some(final_child) => final_child,
|
||||
None => {
|
||||
return Err(nom::Err::Error(CustomError::MyError(MyError(
|
||||
if children.is_empty() {
|
||||
return Err(nom::Err::Error(CustomError::Static(
|
||||
"Plain lists require at least one element.",
|
||||
))));
|
||||
)));
|
||||
}
|
||||
};
|
||||
let final_item_context = ContextElement::ConsumeTrailingWhitespace(false);
|
||||
let final_item_context = parser_context.with_additional_node(&final_item_context);
|
||||
let (remaining, (_, reparsed_final_item)) =
|
||||
parser_with_context!(plain_list_item)(&final_item_context)(final_child_start)?;
|
||||
children.push((final_child_start, reparsed_final_item));
|
||||
|
||||
let (remaining, _trailing_ws) =
|
||||
maybe_consume_trailing_whitespace_if_not_exiting(context, remaining)?;
|
||||
@@ -190,10 +222,10 @@ fn plain_list_item<'b, 'g, 'r, 's>(
|
||||
) -> Res<OrgSource<'s>, (PlainListType, PlainListItem<'s>)> {
|
||||
start_of_line(input)?;
|
||||
let (remaining, (indent_level, _leading_whitespace)) = indentation_level(context, input)?;
|
||||
let (remaining, (bullet_type, bull)) = verify(
|
||||
parser_with_context!(bullet)(context),
|
||||
|(_bullet_type, bull)| !Into::<&str>::into(bull).starts_with('*') || indent_level > 0,
|
||||
)(remaining)?;
|
||||
let (remaining, (bullet_type, bull)) =
|
||||
verify(bind_context!(bullet, context), |(_bullet_type, bull)| {
|
||||
!Into::<&str>::into(bull).starts_with('*') || indent_level > 0
|
||||
})(remaining)?;
|
||||
|
||||
let (remaining, maybe_counter_set) =
|
||||
opt(tuple((space1, tag("[@"), counter_set_value, tag("]"))))(remaining)?;
|
||||
@@ -202,7 +234,7 @@ fn plain_list_item<'b, 'g, 'r, 's>(
|
||||
let (remaining, maybe_checkbox) = opt(tuple((space1, item_checkbox)))(remaining)?;
|
||||
|
||||
let (remaining, maybe_tag) = if let BulletType::Unordered = bullet_type {
|
||||
opt(tuple((space1, parser_with_context!(item_tag)(context))))(remaining)?
|
||||
opt(tuple((space1, bind_context!(item_tag, context))))(remaining)?
|
||||
} else {
|
||||
(remaining, None)
|
||||
};
|
||||
@@ -214,6 +246,12 @@ fn plain_list_item<'b, 'g, 'r, 's>(
|
||||
};
|
||||
|
||||
let exit_matcher = plain_list_item_end(indent_level);
|
||||
let final_item_whitespace_cutoff = final_item_whitespace_cutoff(indent_level);
|
||||
let final_whitespace_context = ContextElement::ExitMatcherNode(ExitMatcherNode {
|
||||
class: ExitClass::Beta,
|
||||
exit_matcher: &final_item_whitespace_cutoff,
|
||||
});
|
||||
let final_whitespace_context = context.with_additional_node(&final_whitespace_context);
|
||||
let contexts = [
|
||||
ContextElement::ConsumeTrailingWhitespace(true),
|
||||
ContextElement::ExitMatcherNode(ExitMatcherNode {
|
||||
@@ -221,17 +259,21 @@ fn plain_list_item<'b, 'g, 'r, 's>(
|
||||
exit_matcher: &exit_matcher,
|
||||
}),
|
||||
];
|
||||
let parser_context = context.with_additional_node(&contexts[0]);
|
||||
let parser_context = final_whitespace_context.with_additional_node(&contexts[0]);
|
||||
let parser_context = parser_context.with_additional_node(&contexts[1]);
|
||||
|
||||
let maybe_contentless_item: Res<OrgSource<'_>, ()> = peek(parser_with_context!(
|
||||
detect_contentless_item_contents
|
||||
)(&parser_context))(remaining);
|
||||
let maybe_contentless_item: Res<OrgSource<'_>, ()> =
|
||||
detect_contentless_item_contents(&parser_context, remaining);
|
||||
if let Ok((_rem, _ws)) = maybe_contentless_item {
|
||||
let (remaining, _trailing_ws) = if context.should_consume_trailing_whitespace() {
|
||||
recognize(alt((recognize(many1(blank_line)), eof)))(remaining)?
|
||||
} else {
|
||||
let (remaining, _trailing_ws) = if tuple((
|
||||
blank_line,
|
||||
bind_context!(final_item_whitespace_cutoff, context),
|
||||
))(remaining)
|
||||
.is_ok()
|
||||
{
|
||||
recognize(alt((blank_line, eof)))(remaining)?
|
||||
} else {
|
||||
recognize(alt((recognize(many1(blank_line)), eof)))(remaining)?
|
||||
};
|
||||
let source = get_consumed(input, remaining);
|
||||
return Ok((
|
||||
@@ -259,26 +301,14 @@ fn plain_list_item<'b, 'g, 'r, 's>(
|
||||
.filter(|b| *b == b'\n')
|
||||
.count();
|
||||
|
||||
let (mut remaining, (mut children, _exit_contents)) = many_till(
|
||||
include_input(parser_with_context!(element(true))(&parser_context)),
|
||||
parser_with_context!(exit_matcher_parser)(&parser_context),
|
||||
let (remaining, (children, _exit_contents)) = many_till(
|
||||
include_input(bind_context!(element(true), &parser_context)),
|
||||
bind_context!(exit_matcher_parser, &parser_context),
|
||||
)(remaining)?;
|
||||
|
||||
if !children.is_empty() && !context.should_consume_trailing_whitespace() {
|
||||
let final_item_context = ContextElement::ConsumeTrailingWhitespace(false);
|
||||
let final_item_context = parser_context.with_additional_node(&final_item_context);
|
||||
let (final_child_start, _original_final_child) = children
|
||||
.pop()
|
||||
.expect("if-statement already checked that children was non-empty.");
|
||||
let (remain, reparsed_final_element) = include_input(parser_with_context!(element(true))(
|
||||
&final_item_context,
|
||||
))(final_child_start)?;
|
||||
remaining = remain;
|
||||
children.push(reparsed_final_element);
|
||||
}
|
||||
|
||||
// We have to use the parser_context here to include the whitespace cut-off
|
||||
let (remaining, _trailing_ws) =
|
||||
maybe_consume_trailing_whitespace_if_not_exiting(context, remaining)?;
|
||||
maybe_consume_trailing_whitespace_if_not_exiting(&final_whitespace_context, remaining)?;
|
||||
|
||||
let source = get_consumed(input, remaining);
|
||||
return Ok((
|
||||
@@ -325,7 +355,7 @@ fn bullet<'b, 'g, 'r, 's>(
|
||||
map(tag("+"), |bull| (BulletType::Unordered, bull)),
|
||||
map(
|
||||
recognize(tuple((
|
||||
parser_with_context!(counter)(context),
|
||||
bind_context!(counter, context),
|
||||
alt((tag("."), tag(")"))),
|
||||
))),
|
||||
|bull| (BulletType::Ordered, bull),
|
||||
@@ -380,6 +410,52 @@ fn counter_set_value<'s>(input: OrgSource<'s>) -> Res<OrgSource<'s>, PlainListIt
|
||||
))(input)
|
||||
}
|
||||
|
||||
const fn final_item_whitespace_cutoff(indent_level: IndentationLevel) -> impl ContextMatcher {
|
||||
move |context, input: OrgSource<'_>| {
|
||||
impl_final_item_whitespace_cutoff(context, input, indent_level)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg_attr(
|
||||
feature = "tracing",
|
||||
tracing::instrument(ret, level = "debug", skip(context))
|
||||
)]
|
||||
fn impl_final_item_whitespace_cutoff<'b, 'g, 'r, 's>(
|
||||
context: RefContext<'b, 'g, 'r, 's>,
|
||||
input: OrgSource<'s>,
|
||||
indent_level: IndentationLevel,
|
||||
) -> Res<OrgSource<'s>, OrgSource<'s>> {
|
||||
start_of_line(input)?;
|
||||
// element!(plain_list_end, context, input);
|
||||
|
||||
if let Ok((_remaining, _)) = verify(
|
||||
tuple((
|
||||
opt(blank_line),
|
||||
bind_context!(indentation_level, context),
|
||||
not(multispace1),
|
||||
)),
|
||||
|(_, (depth, _stars), _not_whitespace)| *depth < indent_level,
|
||||
)(input)
|
||||
{
|
||||
return Ok((input, input.take(0)));
|
||||
}
|
||||
|
||||
if let Ok((_remaining, _)) = tuple((
|
||||
opt(blank_line),
|
||||
verify(
|
||||
bind_context!(detect_not_plain_list_item_indent, context),
|
||||
|(depth, _)| *depth == indent_level,
|
||||
),
|
||||
))(input)
|
||||
{
|
||||
return Ok((input, input.take(0)));
|
||||
}
|
||||
|
||||
Err(nom::Err::Error(CustomError::Static(
|
||||
"No whitespace cut-off.",
|
||||
)))
|
||||
}
|
||||
|
||||
#[cfg_attr(
|
||||
feature = "tracing",
|
||||
tracing::instrument(ret, level = "debug", skip(_context))
|
||||
@@ -415,7 +491,7 @@ fn _plain_list_item_end<'b, 'g, 'r, 's>(
|
||||
start_of_line(input)?;
|
||||
recognize(tuple((
|
||||
opt(blank_line),
|
||||
parser_with_context!(line_indented_lte_matcher)(context),
|
||||
bind_context!(line_indented_lte_matcher, context),
|
||||
)))(input)
|
||||
}
|
||||
|
||||
@@ -434,7 +510,7 @@ fn _line_indented_lte<'b, 'g, 'r, 's>(
|
||||
) -> Res<OrgSource<'s>, OrgSource<'s>> {
|
||||
let matched = recognize(verify(
|
||||
tuple((
|
||||
parser_with_context!(indentation_level)(context),
|
||||
bind_context!(indentation_level, context),
|
||||
non_whitespace_character,
|
||||
)),
|
||||
// It is fine that we get the indent level using the number of bytes rather than the number of characters because nom's space0 only matches space and tab (0x20 and 0x09)
|
||||
@@ -460,8 +536,8 @@ fn item_tag<'b, 'g, 'r, 's>(
|
||||
let (remaining, (children, _exit_contents)) = verify(
|
||||
many_till(
|
||||
// TODO: Should this be using a different set like the minimal set?
|
||||
parser_with_context!(standard_set_object)(&parser_context),
|
||||
parser_with_context!(exit_matcher_parser)(&parser_context),
|
||||
bind_context!(standard_set_object, &parser_context),
|
||||
bind_context!(exit_matcher_parser, &parser_context),
|
||||
),
|
||||
|(children, _exit_contents)| !children.is_empty(),
|
||||
)(input)?;
|
||||
@@ -511,7 +587,7 @@ fn item_tag_post_gap<'b, 'g, 'r, 's>(
|
||||
alt((
|
||||
peek(recognize(not(blank_line))),
|
||||
peek(recognize(tuple((many0(blank_line), eof)))),
|
||||
parser_with_context!(exit_matcher_parser)(context),
|
||||
bind_context!(exit_matcher_parser, context),
|
||||
)),
|
||||
),
|
||||
))),
|
||||
@@ -541,7 +617,7 @@ fn detect_contentless_item_contents<'b, 'g, 'r, 's>(
|
||||
) -> Res<OrgSource<'s>, ()> {
|
||||
let (remaining, _) = recognize(many_till(
|
||||
blank_line,
|
||||
parser_with_context!(exit_matcher_parser)(context),
|
||||
bind_context!(exit_matcher_parser, context),
|
||||
))(input)?;
|
||||
Ok((remaining, ()))
|
||||
}
|
||||
|
||||
@@ -20,7 +20,6 @@ use super::util::org_space_or_line_ending;
|
||||
use crate::context::parser_with_context;
|
||||
use crate::context::RefContext;
|
||||
use crate::error::CustomError;
|
||||
use crate::error::MyError;
|
||||
use crate::error::Res;
|
||||
use crate::types::Object;
|
||||
use crate::types::PlainText;
|
||||
@@ -92,7 +91,7 @@ impl<'x> RematchObject<'x> for PlainText<'x> {
|
||||
break;
|
||||
}
|
||||
|
||||
let is_not_whitespace = is_not::<&str, &str, CustomError<_>>(" \t\r\n")(goal);
|
||||
let is_not_whitespace = is_not::<_, _, CustomError>(" \t\r\n")(goal);
|
||||
if let Ok((new_goal, payload)) = is_not_whitespace {
|
||||
let (new_remaining, _) = tuple((
|
||||
tag_no_case(payload),
|
||||
@@ -108,7 +107,7 @@ impl<'x> RematchObject<'x> for PlainText<'x> {
|
||||
}
|
||||
|
||||
let is_whitespace = recognize(many1(alt((
|
||||
recognize(one_of::<&str, &str, CustomError<_>>(" \t")),
|
||||
recognize(one_of::<_, _, CustomError>(" \t")),
|
||||
line_ending,
|
||||
))))(goal);
|
||||
if let Ok((new_goal, _)) = is_whitespace {
|
||||
@@ -118,9 +117,9 @@ impl<'x> RematchObject<'x> for PlainText<'x> {
|
||||
continue;
|
||||
}
|
||||
|
||||
return Err(nom::Err::Error(CustomError::MyError(MyError(
|
||||
return Err(nom::Err::Error(CustomError::Static(
|
||||
"Target does not match.",
|
||||
))));
|
||||
)));
|
||||
}
|
||||
|
||||
let source = get_consumed(input, remaining);
|
||||
|
||||
@@ -21,7 +21,6 @@ use crate::context::ExitClass;
|
||||
use crate::context::ExitMatcherNode;
|
||||
use crate::context::RefContext;
|
||||
use crate::error::CustomError;
|
||||
use crate::error::MyError;
|
||||
use crate::error::Res;
|
||||
use crate::parser::util::get_consumed;
|
||||
use crate::parser::util::immediate_in_section;
|
||||
@@ -39,9 +38,9 @@ pub(crate) fn property_drawer<'b, 'g, 'r, 's>(
|
||||
input: OrgSource<'s>,
|
||||
) -> Res<OrgSource<'s>, PropertyDrawer<'s>> {
|
||||
if immediate_in_section(context, "property-drawer") {
|
||||
return Err(nom::Err::Error(CustomError::MyError(MyError(
|
||||
return Err(nom::Err::Error(CustomError::Static(
|
||||
"Cannot nest objects of the same element",
|
||||
))));
|
||||
)));
|
||||
}
|
||||
let (
|
||||
remaining,
|
||||
|
||||
@@ -21,7 +21,6 @@ use crate::context::ExitMatcherNode;
|
||||
use crate::context::List;
|
||||
use crate::context::RefContext;
|
||||
use crate::error::CustomError;
|
||||
use crate::error::MyError;
|
||||
use crate::error::Res;
|
||||
use crate::parser::util::get_consumed;
|
||||
use crate::types::Object;
|
||||
@@ -52,9 +51,7 @@ pub(crate) fn radio_link<'b, 'g, 'r, 's>(
|
||||
));
|
||||
}
|
||||
}
|
||||
Err(nom::Err::Error(CustomError::MyError(MyError(
|
||||
"NoRadioLink",
|
||||
))))
|
||||
Err(nom::Err::Error(CustomError::Static("NoRadioLink")))
|
||||
}
|
||||
|
||||
#[cfg_attr(
|
||||
@@ -98,9 +95,9 @@ pub(crate) fn rematch_target<'x, 'b, 'g, 'r, 's>(
|
||||
new_matches.push(new_match);
|
||||
}
|
||||
_ => {
|
||||
return Err(nom::Err::Error(CustomError::MyError(MyError(
|
||||
return Err(nom::Err::Error(CustomError::Static(
|
||||
"OnlyMinimalSetObjectsAllowed",
|
||||
))));
|
||||
)));
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -43,7 +43,6 @@ use crate::context::ExitMatcherNode;
|
||||
use crate::context::List;
|
||||
use crate::context::RefContext;
|
||||
use crate::error::CustomError;
|
||||
use crate::error::MyError;
|
||||
use crate::error::Res;
|
||||
use crate::types::LinkType;
|
||||
use crate::types::Object;
|
||||
@@ -163,11 +162,7 @@ fn parse_path_reg<'b, 'g, 'r, 's>(
|
||||
parser_with_context!(protocol_path_reg)(context),
|
||||
fuzzy_path_reg,
|
||||
))(replaced_input)
|
||||
.map_err(|_| {
|
||||
nom::Err::Error(CustomError::MyError(MyError(
|
||||
"No pathreg match after replacement.",
|
||||
)))
|
||||
})?;
|
||||
.map_err(|_| nom::Err::Error(CustomError::Static("No pathreg match after replacement.")))?;
|
||||
let remaining = input.take(input.len());
|
||||
let link_type = match link.link_type {
|
||||
LinkType::Protocol(protocol) => LinkType::Protocol(protocol.into_owned().into()),
|
||||
@@ -483,7 +478,5 @@ fn impl_path_reg_end<'b, 'g, 'r, 's>(
|
||||
}
|
||||
}
|
||||
|
||||
Err(nom::Err::Error(CustomError::MyError(MyError(
|
||||
"No path reg end",
|
||||
))))
|
||||
Err(nom::Err::Error(CustomError::Static("No path reg end")))
|
||||
}
|
||||
|
||||
@@ -26,7 +26,6 @@ use crate::context::ExitMatcherNode;
|
||||
use crate::context::Matcher;
|
||||
use crate::context::RefContext;
|
||||
use crate::error::CustomError;
|
||||
use crate::error::MyError;
|
||||
use crate::error::Res;
|
||||
use crate::parser::util::get_consumed;
|
||||
use crate::types::Object;
|
||||
@@ -39,7 +38,7 @@ pub(crate) fn detect_subscript_or_superscript<'s>(input: OrgSource<'s>) -> Res<O
|
||||
// This does not have to detect all valid subscript/superscript but all that it detects must be valid.
|
||||
let (remaining, _) = one_of("_^")(input)?;
|
||||
pre(input)?;
|
||||
if tag::<_, _, CustomError<_>>("*")(remaining).is_ok() {
|
||||
if tag::<_, _, CustomError>("*")(remaining).is_ok() {
|
||||
return Ok((input, ()));
|
||||
}
|
||||
let (remaining, _) = opt(one_of("+-"))(remaining)?;
|
||||
@@ -232,9 +231,9 @@ fn _script_with_braces_end<'b, 'g, 'r, 's>(
|
||||
let current_depth = input.get_brace_depth() - starting_brace_depth;
|
||||
if current_depth > 0 {
|
||||
// Its impossible for the next character to end the subscript or superscript if we're any amount of braces deep
|
||||
return Err(nom::Err::Error(CustomError::MyError(MyError(
|
||||
return Err(nom::Err::Error(CustomError::Static(
|
||||
"Not a valid end for subscript or superscript.",
|
||||
))));
|
||||
)));
|
||||
}
|
||||
if current_depth < 0 {
|
||||
// This shouldn't be possible because if depth is 0 then a closing brace should end the subscript or superscript.
|
||||
@@ -282,12 +281,12 @@ fn _script_with_parenthesis_end<'s>(
|
||||
unreachable!("Exceeded citation key suffix bracket depth.")
|
||||
}
|
||||
if current_depth == 0 {
|
||||
let close_parenthesis = tag::<&str, OrgSource<'_>, CustomError<OrgSource<'_>>>(")")(input);
|
||||
let close_parenthesis = tag::<_, _, CustomError>(")")(input);
|
||||
if close_parenthesis.is_ok() {
|
||||
return close_parenthesis;
|
||||
}
|
||||
}
|
||||
Err(nom::Err::Error(CustomError::MyError(MyError(
|
||||
Err(nom::Err::Error(CustomError::Static(
|
||||
"No script parenthesis end.",
|
||||
))))
|
||||
)))
|
||||
}
|
||||
|
||||
@@ -13,7 +13,6 @@ use crate::context::ExitClass;
|
||||
use crate::context::ExitMatcherNode;
|
||||
use crate::context::RefContext;
|
||||
use crate::error::CustomError;
|
||||
use crate::error::MyError;
|
||||
use crate::error::Res;
|
||||
use crate::parser::util::get_consumed;
|
||||
use crate::types::Target;
|
||||
@@ -42,9 +41,9 @@ pub(crate) fn target<'b, 'g, 'r, 's>(
|
||||
.get_preceding_character()
|
||||
.expect("We cannot be at the start of the file because we are inside a target.");
|
||||
if preceding_character.is_whitespace() {
|
||||
return Err(nom::Err::Error(CustomError::MyError(MyError(
|
||||
return Err(nom::Err::Error(CustomError::Static(
|
||||
"Targets cannot end with whitespace.",
|
||||
))));
|
||||
)));
|
||||
}
|
||||
let (remaining, _) = tag(">>")(remaining)?;
|
||||
let (remaining, _trailing_whitespace) =
|
||||
|
||||
@@ -34,7 +34,6 @@ use crate::context::ExitMatcherNode;
|
||||
use crate::context::List;
|
||||
use crate::context::RefContext;
|
||||
use crate::error::CustomError;
|
||||
use crate::error::MyError;
|
||||
use crate::error::Res;
|
||||
use crate::parser::radio_link::rematch_target;
|
||||
use crate::parser::util::exit_matcher_parser;
|
||||
@@ -234,9 +233,9 @@ fn _text_markup_object<'b, 'g, 'r, 's, 'c>(
|
||||
#[cfg(feature = "tracing")]
|
||||
let _enter = span.enter();
|
||||
if exit_matcher_parser(context, remaining).is_ok() {
|
||||
return Err(nom::Err::Error(CustomError::MyError(MyError(
|
||||
return Err(nom::Err::Error(CustomError::Static(
|
||||
"Parent exit matcher is triggering.",
|
||||
))));
|
||||
)));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -290,9 +289,9 @@ fn _text_markup_string<'b, 'g, 'r, 's, 'c>(
|
||||
#[cfg(feature = "tracing")]
|
||||
let _enter = span.enter();
|
||||
if exit_matcher_parser(context, remaining).is_ok() {
|
||||
return Err(nom::Err::Error(CustomError::MyError(MyError(
|
||||
return Err(nom::Err::Error(CustomError::Static(
|
||||
"Parent exit matcher is triggering.",
|
||||
))));
|
||||
)));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -321,9 +320,9 @@ fn pre<'b, 'g, 'r, 's>(
|
||||
// If None, we are at the start of the file which is technically the beginning of a line.
|
||||
Some('-') | Some('(') | Some('{') | Some('\'') | Some('"') => {}
|
||||
Some(_) => {
|
||||
return Err(nom::Err::Error(CustomError::MyError(MyError(
|
||||
return Err(nom::Err::Error(CustomError::Static(
|
||||
"Not a valid pre character for text markup.",
|
||||
))));
|
||||
)));
|
||||
}
|
||||
None => unreachable!(), // None is for start of file, which should already be handled by the start_of_line matcher above.
|
||||
};
|
||||
@@ -360,9 +359,9 @@ fn _text_markup_end<'b, 'g, 'r, 's, 'c>(
|
||||
contents_start_offset: usize,
|
||||
) -> Res<OrgSource<'s>, OrgSource<'s>> {
|
||||
if input.get_byte_offset() == contents_start_offset {
|
||||
return Err(nom::Err::Error(CustomError::MyError(MyError(
|
||||
return Err(nom::Err::Error(CustomError::Static(
|
||||
"Text markup cannot be empty",
|
||||
))));
|
||||
)));
|
||||
}
|
||||
not(preceded_by_whitespace(false))(input)?;
|
||||
let (remaining, _marker) = terminated(
|
||||
@@ -495,9 +494,9 @@ fn _rematch_text_markup_object<'b, 'g, 'r, 's, 'x>(
|
||||
#[cfg(feature = "tracing")]
|
||||
let _enter = span.enter();
|
||||
if exit_matcher_parser(context, remaining).is_ok() {
|
||||
return Err(nom::Err::Error(CustomError::MyError(MyError(
|
||||
return Err(nom::Err::Error(CustomError::Static(
|
||||
"Parent exit matcher is triggering.",
|
||||
))));
|
||||
)));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -20,7 +20,6 @@ use crate::context::parser_with_context;
|
||||
use crate::context::ContextElement;
|
||||
use crate::context::RefContext;
|
||||
use crate::error::CustomError;
|
||||
use crate::error::MyError;
|
||||
use crate::error::Res;
|
||||
use crate::types::IndentationLevel;
|
||||
|
||||
@@ -129,9 +128,7 @@ pub(crate) fn start_of_line<'s>(input: OrgSource<'s>) -> Res<OrgSource<'s>, ()>
|
||||
if input.is_at_start_of_line() {
|
||||
Ok((input, ()))
|
||||
} else {
|
||||
Err(nom::Err::Error(CustomError::MyError(MyError(
|
||||
"Not at start of line",
|
||||
))))
|
||||
Err(nom::Err::Error(CustomError::Static("Not at start of line")))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -152,9 +149,9 @@ fn _preceded_by_whitespace<'s>(
|
||||
.map(|c| c.is_whitespace() || c == '\u{200B}') // 200B = Zero-width space
|
||||
.unwrap_or(allow_start_of_file)
|
||||
{
|
||||
return Err(nom::Err::Error(CustomError::MyError(MyError(
|
||||
return Err(nom::Err::Error(CustomError::Static(
|
||||
"Must be preceded by a whitespace character.",
|
||||
))));
|
||||
)));
|
||||
}
|
||||
Ok((input, ()))
|
||||
}
|
||||
@@ -195,9 +192,7 @@ pub(crate) fn text_until_exit<'b, 'g, 'r, 's>(
|
||||
|
||||
#[allow(dead_code)]
|
||||
fn not_yet_implemented() -> Res<OrgSource<'static>, ()> {
|
||||
Err(nom::Err::Error(CustomError::MyError(MyError(
|
||||
"Not implemented yet.",
|
||||
))))
|
||||
Err(nom::Err::Error(CustomError::Static("Not implemented yet.")))
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
@@ -205,9 +200,7 @@ fn not_yet_implemented() -> Res<OrgSource<'static>, ()> {
|
||||
/// Text from the current point until the next line break or end of file
|
||||
///
|
||||
/// Useful for debugging.
|
||||
fn text_until_eol<'r, 's>(
|
||||
input: OrgSource<'s>,
|
||||
) -> Result<&'s str, nom::Err<CustomError<OrgSource<'s>>>> {
|
||||
fn text_until_eol<'r, 's>(input: OrgSource<'s>) -> Result<&'s str, nom::Err<CustomError>> {
|
||||
let line = recognize(many_till(anychar, alt((line_ending, eof))))(input)
|
||||
.map(|(_remaining, line)| Into::<&str>::into(line))?;
|
||||
Ok(line.trim())
|
||||
@@ -250,6 +243,10 @@ pub(crate) fn org_line_ending(input: OrgSource<'_>) -> Res<OrgSource<'_>, OrgSou
|
||||
}
|
||||
|
||||
/// Match the whitespace at the beginning of a line and give it an indentation level.
|
||||
#[cfg_attr(
|
||||
feature = "tracing",
|
||||
tracing::instrument(ret, level = "debug", skip(context))
|
||||
)]
|
||||
pub(crate) fn indentation_level<'s>(
|
||||
context: RefContext<'_, '_, '_, 's>,
|
||||
input: OrgSource<'s>,
|
||||
|
||||
@@ -78,7 +78,7 @@ enum RemoveLineBreakState {
|
||||
|
||||
/// Removes all whitespace from a string if any line breaks are present.
|
||||
///
|
||||
/// Example: "foo bar" => "foo bar" but "foo \n bar" => "foobar".
|
||||
/// Example: "foo bar" => "foo bar" but "foo \n bar" => "foo bar".
|
||||
pub(crate) fn coalesce_whitespace_if_line_break(input: &str) -> Cow<'_, str> {
|
||||
let mut state = CoalesceWhitespaceIfLineBreakState::Normal;
|
||||
for (offset, c) in input.char_indices() {
|
||||
@@ -515,4 +515,25 @@ mod tests {
|
||||
assert!(matches!(output, Cow::Borrowed(_)));
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_coalesce_whitespace_if_line_break() -> Result<(), Box<dyn std::error::Error>> {
|
||||
assert_eq!(coalesce_whitespace_if_line_break("foo bar"), "foo bar");
|
||||
assert_eq!(coalesce_whitespace_if_line_break("foo \n bar"), "foo bar");
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_remove_whitespace_if_line_break() -> Result<(), Box<dyn std::error::Error>> {
|
||||
assert_eq!(remove_whitespace_if_line_break("foo bar"), "foo bar");
|
||||
assert_eq!(remove_whitespace_if_line_break("foo \n bar"), "foobar");
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_remove_line_break() -> Result<(), Box<dyn std::error::Error>> {
|
||||
assert_eq!(remove_line_break("foo bar"), "foo bar");
|
||||
assert_eq!(remove_line_break("foo \n bar"), "foo bar");
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user