Compare commits
50 Commits
f592b73ae7
...
remove_tek
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
fcd63b1231 | ||
|
|
743b4c9982 | ||
|
|
7eccf8fccd | ||
|
|
a40a504f94 | ||
|
|
80d77ff5d6 | ||
|
|
ee92049e5d | ||
|
|
510985e97c | ||
|
|
949d0989f4 | ||
|
|
2a4d22bdd4 | ||
|
|
7a903acedc | ||
|
|
5171326d63 | ||
|
|
67f79aeb51 | ||
|
|
b2383d9f93 | ||
|
|
9e2a323f6f | ||
|
|
0fcb3f73f9 | ||
|
|
bfc9e7f58d | ||
|
|
b5f0521b56 | ||
|
|
2048d8f0b6 | ||
|
|
466716881e | ||
|
|
eb9c582fa5 | ||
|
|
214e895d85 | ||
|
|
db3086743c | ||
|
|
207a0546b0 | ||
|
|
e9480fd156 | ||
|
|
28aca041f7 | ||
|
|
d82def2a70 | ||
|
|
d471f7178b | ||
|
|
2c5c26c55f | ||
|
|
7944659802 | ||
|
|
58aca53144 | ||
|
|
6f2d90162b | ||
|
|
f170a557ed | ||
|
|
eaa38ce772 | ||
|
|
a6d742a536 | ||
|
|
45be9e7bde | ||
|
|
f6c895319f | ||
|
|
2682779534 | ||
|
|
b48d472546 | ||
|
|
ea6faf728c | ||
|
|
f4ea1b7303 | ||
|
|
80b55fdd45 | ||
|
|
f426e32798 | ||
|
|
66037356c5 | ||
|
|
1bcd1895c0 | ||
|
|
e3d38cfbe2 | ||
|
|
2ba0dc49be | ||
|
|
9df40fb13f | ||
|
|
cc671925db | ||
|
|
950baa9d5d | ||
|
|
56865c68fc |
@@ -99,20 +99,41 @@ spec:
|
||||
runAfter:
|
||||
- fetch-repository
|
||||
- name: run-image
|
||||
taskRef:
|
||||
name: run-docker-image
|
||||
taskSpec:
|
||||
metadata: {}
|
||||
stepTemplate:
|
||||
name: ""
|
||||
workingDir: "$(workspaces.source.path)"
|
||||
workspaces:
|
||||
- name: source
|
||||
mountPath: /source
|
||||
- name: cargo-cache
|
||||
mountPath: /usr/local/cargo/registry
|
||||
optional: true
|
||||
steps:
|
||||
- name: run
|
||||
image: "$(params.IMAGE)"
|
||||
command: []
|
||||
args:
|
||||
[
|
||||
--no-default-features,
|
||||
--features,
|
||||
compare,
|
||||
--no-fail-fast,
|
||||
--lib,
|
||||
--test,
|
||||
test_loader,
|
||||
]
|
||||
workspaces:
|
||||
- name: source
|
||||
workspace: git-source
|
||||
- name: cargo-cache
|
||||
workspace: cargo-cache
|
||||
params:
|
||||
- name: IMAGE
|
||||
value: "$(params.image-name):$(tasks.fetch-repository.results.commit)"
|
||||
runAfter:
|
||||
- build-image
|
||||
params:
|
||||
- name: args
|
||||
value: [--no-fail-fast, --lib, --test, test_loader]
|
||||
- name: docker-image
|
||||
value: "$(params.image-name):$(tasks.fetch-repository.results.commit)"
|
||||
finally:
|
||||
- name: report-success
|
||||
when:
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "organic"
|
||||
version = "0.1.2"
|
||||
version = "0.1.4"
|
||||
authors = ["Tom Alexander <tom@fizz.buzz>"]
|
||||
description = "An org-mode parser."
|
||||
edition = "2021"
|
||||
@@ -13,8 +13,7 @@ resolver = "2"
|
||||
include = [
|
||||
"LICENSE",
|
||||
"**/*.rs",
|
||||
"Cargo.toml",
|
||||
"tests/*"
|
||||
"Cargo.toml"
|
||||
]
|
||||
|
||||
[lib]
|
||||
@@ -40,7 +39,7 @@ tracing-subscriber = { version = "0.3.17", optional = true, features = ["env-fil
|
||||
walkdir = "2.3.3"
|
||||
|
||||
[features]
|
||||
default = ["compare"]
|
||||
default = []
|
||||
compare = []
|
||||
tracing = ["dep:opentelemetry", "dep:opentelemetry-otlp", "dep:opentelemetry-semantic-conventions", "dep:tokio", "dep:tracing", "dep:tracing-opentelemetry", "dep:tracing-subscriber"]
|
||||
|
||||
|
||||
10
Makefile
10
Makefile
@@ -35,12 +35,12 @@ clean:
|
||||
|
||||
.PHONY: test
|
||||
test:
|
||||
> cargo test --no-fail-fast --lib --test test_loader -- --test-threads $(TESTJOBS)
|
||||
> cargo test --no-default-features --features compare --no-fail-fast --lib --test test_loader -- --test-threads $(TESTJOBS)
|
||||
|
||||
.PHONY: dockertest
|
||||
dockertest:
|
||||
> $(MAKE) -C docker/organic_test
|
||||
> docker run --init --rm -i -t -v "$$(readlink -f ./):/source:ro" --mount source=cargo-cache,target=/usr/local/cargo/registry --mount source=rust-cache,target=/target --env CARGO_TARGET_DIR=/target -w /source organic-test --no-fail-fast --lib --test test_loader -- --test-threads $(TESTJOBS)
|
||||
> docker run --init --rm -i -t --read-only -v "$$(readlink -f ./):/source:ro" --mount type=tmpfs,destination=/tmp --mount source=cargo-cache,target=/usr/local/cargo/registry --mount source=rust-cache,target=/target --env CARGO_TARGET_DIR=/target -w /source organic-test --no-default-features --features compare --no-fail-fast --lib --test test_loader -- --test-threads $(TESTJOBS)
|
||||
|
||||
.PHONY: dockerclean
|
||||
dockerclean:
|
||||
@@ -49,18 +49,18 @@ dockerclean:
|
||||
|
||||
.PHONY: integrationtest
|
||||
integrationtest:
|
||||
> cargo test --no-fail-fast --test test_loader -- --test-threads $(TESTJOBS)
|
||||
> cargo test --no-default-features --features compare --no-fail-fast --test test_loader -- --test-threads $(TESTJOBS)
|
||||
|
||||
.PHONY: unittest
|
||||
unittest:
|
||||
> cargo test --lib -- --test-threads $(TESTJOBS)
|
||||
> cargo test --no-default-features --lib -- --test-threads $(TESTJOBS)
|
||||
|
||||
.PHONY: jaeger
|
||||
jaeger:
|
||||
# 4317 for OTLP gRPC, 4318 for OTLP HTTP. We currently use gRPC but I forward both ports regardless.
|
||||
#
|
||||
# These flags didn't help even though they seem like they would: --collector.queue-size=20000 --collector.num-workers=100
|
||||
> docker run -d --rm --name organicdocker -p 4317:4317 -p 4318:4318 -p 16686:16686 -e COLLECTOR_OTLP_ENABLED=true jaegertracing/all-in-one:1.47 --collector.grpc-server.max-message-size=20000000 --collector.otlp.grpc.max-message-size=20000000
|
||||
> docker run -d --rm --name organicdocker --read-only -p 4317:4317 -p 4318:4318 -p 16686:16686 -e COLLECTOR_OTLP_ENABLED=true jaegertracing/all-in-one:1.47 --collector.grpc-server.max-message-size=20000000 --collector.otlp.grpc.max-message-size=20000000
|
||||
|
||||
.PHONY: jaegerweb
|
||||
jaegerweb:
|
||||
|
||||
13
build.rs
13
build.rs
@@ -1,10 +1,16 @@
|
||||
#[cfg(feature = "compare")]
|
||||
use std::env;
|
||||
#[cfg(feature = "compare")]
|
||||
use std::fs::File;
|
||||
#[cfg(feature = "compare")]
|
||||
use std::io::Write;
|
||||
#[cfg(feature = "compare")]
|
||||
use std::path::Path;
|
||||
|
||||
#[cfg(feature = "compare")]
|
||||
use walkdir::WalkDir;
|
||||
|
||||
#[cfg(feature = "compare")]
|
||||
fn main() {
|
||||
let out_dir = env::var("OUT_DIR").unwrap();
|
||||
let destination = Path::new(&out_dir).join("tests.rs");
|
||||
@@ -31,6 +37,10 @@ fn main() {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "compare"))]
|
||||
fn main() {}
|
||||
|
||||
#[cfg(feature = "compare")]
|
||||
fn write_test(test_file: &mut File, test: &walkdir::DirEntry) {
|
||||
let test_name = test
|
||||
.path()
|
||||
@@ -55,6 +65,7 @@ fn write_test(test_file: &mut File, test: &walkdir::DirEntry) {
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
#[cfg(feature = "compare")]
|
||||
fn write_header(test_file: &mut File) {
|
||||
write!(
|
||||
test_file,
|
||||
@@ -70,12 +81,12 @@ use organic::parser::sexp::sexp_with_padding;
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
#[cfg(feature = "compare")]
|
||||
fn is_expect_fail(name: &str) -> Option<&str> {
|
||||
match name {
|
||||
"autogen_greater_element_drawer_drawer_with_headline_inside" => Some("Apparently lines with :end: become their own paragraph. This odd behavior needs to be investigated more."),
|
||||
"autogen_element_container_priority_footnote_definition_dynamic_block" => Some("Apparently broken begin lines become their own paragraph."),
|
||||
"autogen_lesser_element_paragraphs_paragraph_with_backslash_line_breaks" => Some("The text we're getting out of the parse tree is already processed to remove line breaks, so our comparison needs to take that into account."),
|
||||
"autogen_sections_and_headings_empty_section" => Some("We are not yet handling empty sections properly."),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -29,8 +29,8 @@ endif
|
||||
# NOTE: This target will write to folders underneath the git-root
|
||||
.PHONY: run
|
||||
run: build
|
||||
docker run --rm --init -v "$$(readlink -f ../../):/source" --workdir=/source $(IMAGE_NAME)
|
||||
docker run --rm --init --read-only --mount type=tmpfs,destination=/tmp -v "$$(readlink -f ../../):/source" --workdir=/source $(IMAGE_NAME)
|
||||
|
||||
.PHONY: shell
|
||||
shell: build
|
||||
docker run --rm -i -t --entrypoint /bin/sh -v "$$(readlink -f ../../):/source" --workdir=/source $(IMAGE_NAME)
|
||||
docker run --rm -i -t --entrypoint /bin/sh --mount type=tmpfs,destination=/tmp -v "$$(readlink -f ../../):/source" --workdir=/source $(IMAGE_NAME)
|
||||
|
||||
@@ -30,8 +30,8 @@ endif
|
||||
# NOTE: This target will write to folders underneath the git-root
|
||||
.PHONY: run
|
||||
run: build
|
||||
docker run --rm --init -v "$$(readlink -f ../../):/source" --workdir=/source --mount source=cargo-cache,target=/usr/local/cargo/registry $(IMAGE_NAME)
|
||||
docker run --rm --init --read-only --mount type=tmpfs,destination=/tmp -v "$$(readlink -f ../../):/source" --workdir=/source --mount source=cargo-cache,target=/usr/local/cargo/registry $(IMAGE_NAME)
|
||||
|
||||
.PHONY: shell
|
||||
shell: build
|
||||
docker run --rm -i -t --entrypoint /bin/sh -v "$$(readlink -f ../../):/source" --workdir=/source --mount source=cargo-cache,target=/usr/local/cargo/registry $(IMAGE_NAME)
|
||||
docker run --rm -i -t --entrypoint /bin/sh --mount type=tmpfs,destination=/tmp -v "$$(readlink -f ../../):/source" --workdir=/source --mount source=cargo-cache,target=/usr/local/cargo/registry $(IMAGE_NAME)
|
||||
|
||||
@@ -26,6 +26,7 @@ RUN make DESTDIR="/root/dist" install
|
||||
|
||||
|
||||
FROM rustlang/rust:nightly-alpine3.17
|
||||
ENV LANG=en_US.UTF-8
|
||||
RUN apk add --no-cache musl-dev ncurses gnutls
|
||||
RUN cargo install --locked --no-default-features --features ci-autoclean cargo-cache
|
||||
COPY --from=build-emacs /root/dist/ /
|
||||
|
||||
@@ -29,8 +29,8 @@ endif
|
||||
|
||||
.PHONY: run
|
||||
run: build
|
||||
docker run --rm --init -v "$$(readlink -f ../../):/source:ro" --workdir=/source --mount source=cargo-cache,target=/usr/local/cargo/registry --mount source=rust-cache,target=/target --env CARGO_TARGET_DIR=/target $(IMAGE_NAME) --no-fail-fast --lib --test test_loader
|
||||
docker run --rm --init --read-only --mount type=tmpfs,destination=/tmp -v "$$(readlink -f ../../):/source:ro" --workdir=/source --mount source=cargo-cache,target=/usr/local/cargo/registry --mount source=rust-cache,target=/target --env CARGO_TARGET_DIR=/target $(IMAGE_NAME) --no-default-features --features compare --no-fail-fast --lib --test test_loader
|
||||
|
||||
.PHONY: shell
|
||||
shell: build
|
||||
docker run --rm -i -t --entrypoint /bin/sh -v "$$(readlink -f ../../):/source:ro" --workdir=/source --mount source=cargo-cache,target=/usr/local/cargo/registry --mount source=rust-cache,target=/target --env CARGO_TARGET_DIR=/target $(IMAGE_NAME)
|
||||
docker run --rm -i -t --entrypoint /bin/sh --mount type=tmpfs,destination=/tmp -v "$$(readlink -f ../../):/source:ro" --workdir=/source --mount source=cargo-cache,target=/usr/local/cargo/registry --mount source=rust-cache,target=/target --env CARGO_TARGET_DIR=/target $(IMAGE_NAME)
|
||||
|
||||
3
elisp_snippets/dump_org_element_affiliated_keywords.el
Normal file
3
elisp_snippets/dump_org_element_affiliated_keywords.el
Normal file
@@ -0,0 +1,3 @@
|
||||
(dolist (var org-element-affiliated-keywords)
|
||||
(message "\"%s\"," (downcase var))
|
||||
)
|
||||
@@ -0,0 +1,18 @@
|
||||
#+begin_defun
|
||||
foo
|
||||
#+begin_lorem
|
||||
,#+begin_center
|
||||
bar
|
||||
,#+end_center
|
||||
ipsum
|
||||
#+end_lorem
|
||||
baz
|
||||
#+end_defun
|
||||
|
||||
#+begin_center
|
||||
#+begin_quote
|
||||
#+begin_center
|
||||
lorem
|
||||
#+end_center
|
||||
#+end_quote
|
||||
#+end_center
|
||||
@@ -0,0 +1,12 @@
|
||||
#+begin_defun
|
||||
foo
|
||||
#+begin_lorem
|
||||
ipsum
|
||||
#+end_lorem
|
||||
bar
|
||||
#+begin_center
|
||||
#+begin_quote
|
||||
baz
|
||||
#+end_quote
|
||||
#+end_center
|
||||
#+end_defun
|
||||
@@ -0,0 +1,5 @@
|
||||
#+begin_defun
|
||||
foo
|
||||
|
||||
{{{bar(baz)}}}
|
||||
#+end_defun
|
||||
@@ -0,0 +1,2 @@
|
||||
- foo ::
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
- foo :: bar
|
||||
- cat ::
|
||||
dog
|
||||
- lorem
|
||||
:: ipsum
|
||||
-
|
||||
lorem :: ipsum
|
||||
- dolar *bold* foo :: ipsum
|
||||
- big gap ::
|
||||
|
||||
stuff
|
||||
@@ -0,0 +1 @@
|
||||
- {{{foo(bar)}}} :: baz
|
||||
@@ -0,0 +1,2 @@
|
||||
- foo
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
** foo
|
||||
:PROPERTIES:
|
||||
:DESCRIPTION: lorem
|
||||
:ALT_TITLE: ipsum
|
||||
:END:
|
||||
|
||||
bar
|
||||
@@ -0,0 +1 @@
|
||||
%%(foo bar) ; baz
|
||||
@@ -0,0 +1,15 @@
|
||||
#+name: foo
|
||||
#+caption: bar
|
||||
#+caption: baz
|
||||
|
||||
[[file:lorem/ipsum.png]]
|
||||
|
||||
#+name: cat
|
||||
#+foo: dog
|
||||
[[file:lorem/ipsum.png]]
|
||||
|
||||
#+name: cat
|
||||
#+foo: dog
|
||||
|
||||
|
||||
foo
|
||||
52
org_mode_samples/object/plain_link/empty_links.org
Normal file
52
org_mode_samples/object/plain_link/empty_links.org
Normal file
@@ -0,0 +1,52 @@
|
||||
non-link text
|
||||
eww://
|
||||
rmail://
|
||||
mhe://
|
||||
irc://
|
||||
info://
|
||||
gnus://
|
||||
docview://
|
||||
bibtex://
|
||||
bbdb://
|
||||
w3m://
|
||||
doi://
|
||||
file+sys://
|
||||
file+emacs://
|
||||
shell://
|
||||
news://
|
||||
mailto://
|
||||
https://
|
||||
http://
|
||||
ftp://
|
||||
help://
|
||||
file://
|
||||
elisp://
|
||||
randomfakeprotocl://
|
||||
non-link text
|
||||
|
||||
|
||||
non-link text
|
||||
eww:
|
||||
rmail:
|
||||
mhe:
|
||||
irc:
|
||||
info:
|
||||
gnus:
|
||||
docview:
|
||||
bibtex:
|
||||
bbdb:
|
||||
w3m:
|
||||
doi:
|
||||
file+sys:
|
||||
file+emacs:
|
||||
shell:
|
||||
news:
|
||||
mailto:
|
||||
https:
|
||||
http:
|
||||
ftp:
|
||||
help:
|
||||
file:
|
||||
elisp:
|
||||
randomfakeprotocl:
|
||||
non-link text
|
||||
@@ -0,0 +1,3 @@
|
||||
mailto:foo@bar.baz.
|
||||
|
||||
mailto:foo@bar.baz....
|
||||
@@ -0,0 +1 @@
|
||||
foo ==>bar=.
|
||||
1
org_mode_samples/unicode/hearts.org
Normal file
1
org_mode_samples/unicode/hearts.org
Normal file
@@ -0,0 +1 @@
|
||||
🧡💛💚💙💜
|
||||
@@ -41,6 +41,7 @@ function launch_container {
|
||||
if [ "$SHELL" != "YES" ]; then
|
||||
local features_joined=$(IFS=","; echo "${features[*]}")
|
||||
additional_args+=(cargo run --no-default-features --features "$features_joined")
|
||||
additional_flags+=(--read-only)
|
||||
else
|
||||
additional_args+=(/bin/sh)
|
||||
additional_flags+=(-t)
|
||||
@@ -50,7 +51,7 @@ function launch_container {
|
||||
additional_flags+=(--env RUST_BACKTRACE=full)
|
||||
fi
|
||||
|
||||
docker run "${additional_flags[@]}" --init --rm -i -v "$($REALPATH ./):/source:ro" --mount source=cargo-cache,target=/usr/local/cargo/registry --mount source=rust-cache,target=/target --env CARGO_TARGET_DIR=/target -w /source --entrypoint "" organic-test "${additional_args[@]}"
|
||||
docker run "${additional_flags[@]}" --init --rm -i --mount type=tmpfs,destination=/tmp -v "$($REALPATH ./):/source:ro" --mount source=cargo-cache,target=/usr/local/cargo/registry --mount source=rust-cache,target=/target --env CARGO_TARGET_DIR=/target -w /source --entrypoint "" organic-test "${additional_args[@]}"
|
||||
}
|
||||
|
||||
main "${@}"
|
||||
|
||||
@@ -33,7 +33,7 @@ function get_test_names {
|
||||
local test_file_full_path=$($REALPATH "$test_file")
|
||||
local relative_to_samples=$($REALPATH --relative-to "$samples_dir" "$test_file_full_path")
|
||||
local without_extension="${relative_to_samples%.org}"
|
||||
echo "${without_extension/\//_}" | tr '[:upper:]' '[:lower:]'
|
||||
echo "autogen_${without_extension//\//_}" | tr '[:upper:]' '[:lower:]'
|
||||
else
|
||||
echo "$test_file" | tr '[:upper:]' '[:lower:]'
|
||||
fi
|
||||
@@ -52,11 +52,11 @@ function launch_container {
|
||||
set -euo pipefail
|
||||
IFS=\$'\n\t'
|
||||
|
||||
cargo test --no-fail-fast --lib --test test_loader "$test" -- --show-output
|
||||
cargo test --no-default-features --features compare --no-fail-fast --lib --test test_loader "$test" -- --show-output
|
||||
EOF
|
||||
)
|
||||
|
||||
docker run "${additional_flags[@]}" --init --rm -v "$($REALPATH ./):/source:ro" --mount source=cargo-cache,target=/usr/local/cargo/registry --mount source=rust-cache,target=/target --env CARGO_TARGET_DIR=/target -w /source --entrypoint "" organic-test sh -c "$init_script"
|
||||
docker run "${additional_flags[@]}" --init --rm --read-only --mount type=tmpfs,destination=/tmp -v "$($REALPATH ./):/source:ro" --mount source=cargo-cache,target=/usr/local/cargo/registry --mount source=rust-cache,target=/target --env CARGO_TARGET_DIR=/target -w /source --entrypoint "" organic-test sh -c "$init_script"
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -12,7 +12,7 @@ function main {
|
||||
|
||||
local test
|
||||
while read test; do
|
||||
cargo test --no-fail-fast --test test_loader "$test" -- --show-output
|
||||
cargo test --no-default-features --features compare --no-fail-fast --test test_loader "$test" -- --show-output
|
||||
done<<<"$test_names"
|
||||
}
|
||||
|
||||
@@ -25,7 +25,7 @@ function get_test_names {
|
||||
local test_file_full_path=$($REALPATH "$test_file")
|
||||
local relative_to_samples=$($REALPATH --relative-to "$samples_dir" "$test_file_full_path")
|
||||
local without_extension="${relative_to_samples%.org}"
|
||||
echo "${without_extension/\//_}" | tr '[:upper:]' '[:lower:]'
|
||||
echo "${without_extension//\//_}" | tr '[:upper:]' '[:lower:]'
|
||||
else
|
||||
echo "$test_file" | tr '[:upper:]' '[:lower:]'
|
||||
fi
|
||||
|
||||
1203
src/compare/diff.rs
1203
src/compare/diff.rs
File diff suppressed because it is too large
Load Diff
@@ -13,7 +13,7 @@ fn is_slice_of(parent: &str, child: &str) -> bool {
|
||||
/// Get the offset into source that the rust object exists at.
|
||||
///
|
||||
/// These offsets are zero-based unlike the elisp ones.
|
||||
pub fn get_offsets<'s, S: Source<'s>>(source: &'s str, rust_object: &'s S) -> (usize, usize) {
|
||||
fn get_offsets<'s, S: Source<'s>>(source: &'s str, rust_object: &'s S) -> (usize, usize) {
|
||||
let rust_object_source = rust_object.get_source();
|
||||
assert!(is_slice_of(source, rust_object_source));
|
||||
let offset = rust_object_source.as_ptr() as usize - source.as_ptr() as usize;
|
||||
@@ -50,8 +50,11 @@ pub fn assert_bounds<'s, S: Source<'s>>(
|
||||
standard_properties.end.ok_or("Token should have an end.")?,
|
||||
);
|
||||
let (rust_begin, rust_end) = get_offsets(source, rust);
|
||||
if (rust_begin + 1) != begin || (rust_end + 1) != end {
|
||||
Err(format!("Rust bounds ({rust_begin}, {rust_end}) do not match emacs bounds ({emacs_begin}, {emacs_end})", rust_begin = rust_begin + 1, rust_end = rust_end + 1, emacs_begin=begin, emacs_end=end))?;
|
||||
let rust_begin_char_offset = (&source[..rust_begin]).chars().count();
|
||||
let rust_end_char_offset =
|
||||
rust_begin_char_offset + (&source[rust_begin..rust_end]).chars().count();
|
||||
if (rust_begin_char_offset + 1) != begin || (rust_end_char_offset + 1) != end {
|
||||
Err(format!("Rust bounds (in chars) ({rust_begin}, {rust_end}) do not match emacs bounds ({emacs_begin}, {emacs_end})", rust_begin = rust_begin_char_offset + 1, rust_end = rust_end_char_offset + 1, emacs_begin=begin, emacs_end=end))?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
@@ -137,3 +140,23 @@ fn maybe_token_to_usize(
|
||||
.flatten() // Outer option is whether or not the param exists, inner option is whether or not it is nil
|
||||
.map_or(Ok(None), |r| r.map(Some))?)
|
||||
}
|
||||
|
||||
pub fn get_property<'s, 'x>(
|
||||
emacs: &'s Token<'s>,
|
||||
key: &'x str,
|
||||
) -> Result<Option<&'s Token<'s>>, Box<dyn std::error::Error>> {
|
||||
let children = emacs.as_list()?;
|
||||
let attributes_child = children
|
||||
.iter()
|
||||
.nth(1)
|
||||
.ok_or("Should have an attributes child.")?;
|
||||
let attributes_map = attributes_child.as_map()?;
|
||||
let prop = attributes_map
|
||||
.get(key)
|
||||
.ok_or(format!("Missing {} attribute.", key))?;
|
||||
match prop.as_atom() {
|
||||
Ok("nil") => return Ok(None),
|
||||
_ => {}
|
||||
};
|
||||
Ok(Some(*prop))
|
||||
}
|
||||
|
||||
@@ -86,7 +86,7 @@ fn run_compare<P: AsRef<str>>(org_contents: P) -> Result<(), Box<dyn std::error:
|
||||
eprintln!(
|
||||
"This program was built with compare disabled. Only parsing with organic, not comparing."
|
||||
);
|
||||
let (remaining, rust_parsed) = document(org_contents.as_ref()).map_err(|e| e.to_string())?;
|
||||
let (_remaining, rust_parsed) = document(org_contents.as_ref()).map_err(|e| e.to_string())?;
|
||||
println!("{:#?}", rust_parsed);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@ use nom::combinator::recognize;
|
||||
use nom::multi::many_till;
|
||||
|
||||
use super::org_source::OrgSource;
|
||||
use super::util::maybe_consume_object_trailing_whitespace_if_not_exiting;
|
||||
use super::Context;
|
||||
use crate::error::Res;
|
||||
use crate::parser::exiting::ExitClass;
|
||||
@@ -26,6 +27,8 @@ pub fn angle_link<'r, 's>(
|
||||
let (remaining, _separator) = tag(":")(remaining)?;
|
||||
let (remaining, path) = path_angle(context, remaining)?;
|
||||
let (remaining, _) = tag(">")(remaining)?;
|
||||
let (remaining, _trailing_whitespace) =
|
||||
maybe_consume_object_trailing_whitespace_if_not_exiting(context, remaining)?;
|
||||
let source = get_consumed(input, remaining);
|
||||
Ok((
|
||||
remaining,
|
||||
|
||||
@@ -2,7 +2,6 @@ use nom::branch::alt;
|
||||
use nom::bytes::complete::tag;
|
||||
use nom::bytes::complete::tag_no_case;
|
||||
use nom::character::complete::anychar;
|
||||
use nom::character::complete::space0;
|
||||
use nom::combinator::opt;
|
||||
use nom::combinator::recognize;
|
||||
use nom::combinator::verify;
|
||||
@@ -14,6 +13,7 @@ use nom::sequence::tuple;
|
||||
use super::citation_reference::must_balance_bracket;
|
||||
use super::org_source::BracketDepth;
|
||||
use super::org_source::OrgSource;
|
||||
use super::util::maybe_consume_object_trailing_whitespace_if_not_exiting;
|
||||
use super::Context;
|
||||
use crate::error::CustomError;
|
||||
use crate::error::Res;
|
||||
@@ -48,7 +48,8 @@ pub fn citation<'r, 's>(
|
||||
parser_with_context!(global_suffix)(context),
|
||||
))))(remaining)?;
|
||||
let (remaining, _) = tag("]")(remaining)?;
|
||||
let (remaining, _) = space0(remaining)?;
|
||||
let (remaining, _trailing_whitespace) =
|
||||
maybe_consume_object_trailing_whitespace_if_not_exiting(context, remaining)?;
|
||||
let source = get_consumed(input, remaining);
|
||||
Ok((
|
||||
remaining,
|
||||
|
||||
@@ -1,9 +1,12 @@
|
||||
use nom::branch::alt;
|
||||
use nom::bytes::complete::tag;
|
||||
use nom::character::complete::anychar;
|
||||
use nom::character::complete::line_ending;
|
||||
use nom::character::complete::space0;
|
||||
use nom::combinator::eof;
|
||||
use nom::combinator::opt;
|
||||
use nom::combinator::recognize;
|
||||
use nom::multi::many_till;
|
||||
use nom::sequence::tuple;
|
||||
|
||||
use super::org_source::OrgSource;
|
||||
@@ -24,6 +27,11 @@ pub fn diary_sexp<'r, 's>(
|
||||
let (remaining, _clock) = tag("%%")(remaining)?;
|
||||
let (remaining, _gap_whitespace) = space0(remaining)?;
|
||||
let (remaining, _sexp) = recognize(sexp)(remaining)?;
|
||||
let (remaining, _trailing_comment) = opt(tuple((
|
||||
space0,
|
||||
tag(";"),
|
||||
many_till(anychar, alt((line_ending, eof))),
|
||||
)))(remaining)?;
|
||||
let (remaining, _trailing_whitespace) =
|
||||
recognize(tuple((space0, alt((line_ending, eof)))))(remaining)?;
|
||||
|
||||
|
||||
@@ -283,10 +283,21 @@ fn _heading<'r, 's>(
|
||||
headline(context, input, parent_stars)?;
|
||||
let section_matcher = parser_with_context!(section)(context);
|
||||
let heading_matcher = parser_with_context!(heading(star_count))(context);
|
||||
let (remaining, children) = many0(alt((
|
||||
map(heading_matcher, DocumentElement::Heading),
|
||||
map(section_matcher, DocumentElement::Section),
|
||||
)))(remaining)?;
|
||||
let (remaining, maybe_section) =
|
||||
opt(map(section_matcher, DocumentElement::Section))(remaining)?;
|
||||
let (remaining, mut children) =
|
||||
many0(map(heading_matcher, DocumentElement::Heading))(remaining)?;
|
||||
if let Some(section) = maybe_section {
|
||||
children.insert(0, section);
|
||||
}
|
||||
let remaining = if children.is_empty() {
|
||||
// Support empty headings
|
||||
let (remain, _ws) = many0(blank_line)(remaining)?;
|
||||
remain
|
||||
} else {
|
||||
remaining
|
||||
};
|
||||
|
||||
let source = get_consumed(input, remaining);
|
||||
Ok((
|
||||
remaining,
|
||||
|
||||
@@ -12,6 +12,7 @@ use super::fixed_width_area::fixed_width_area;
|
||||
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;
|
||||
@@ -62,10 +63,12 @@ fn _element<'r, 's>(
|
||||
let fixed_width_area_matcher = parser_with_context!(fixed_width_area)(context);
|
||||
let horizontal_rule_matcher = parser_with_context!(horizontal_rule)(context);
|
||||
let keyword_matcher = parser_with_context!(keyword)(context);
|
||||
let affiliated_keyword_matcher = parser_with_context!(affiliated_keyword)(context);
|
||||
let paragraph_matcher = parser_with_context!(paragraph)(context);
|
||||
let latex_environment_matcher = parser_with_context!(latex_environment)(context);
|
||||
|
||||
let (remaining, mut affiliated_keywords) = many0(keyword_matcher)(input)?;
|
||||
// TODO: Affiliated keywords cannot be on comments, clocks, headings, inlinetasks, items, node properties, planning, property drawers, sections, and table rows
|
||||
let (remaining, mut affiliated_keywords) = many0(affiliated_keyword_matcher)(input)?;
|
||||
let (remaining, mut element) = match alt((
|
||||
map(plain_list_matcher, Element::PlainList),
|
||||
map(greater_block_matcher, Element::GreaterBlock),
|
||||
@@ -84,6 +87,7 @@ fn _element<'r, 's>(
|
||||
map(fixed_width_area_matcher, Element::FixedWidthArea),
|
||||
map(horizontal_rule_matcher, Element::HorizontalRule),
|
||||
map(latex_environment_matcher, Element::LatexEnvironment),
|
||||
map(keyword_matcher, Element::Keyword),
|
||||
))(remaining)
|
||||
{
|
||||
the_ok @ Ok(_) => the_ok,
|
||||
@@ -93,12 +97,12 @@ fn _element<'r, 's>(
|
||||
the_ok @ Ok(_) => the_ok,
|
||||
Err(_) => {
|
||||
affiliated_keywords.clear();
|
||||
map(keyword_matcher, Element::Keyword)(input)
|
||||
map(affiliated_keyword_matcher, Element::Keyword)(input)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
affiliated_keywords.clear();
|
||||
map(keyword_matcher, Element::Keyword)(input)
|
||||
map(affiliated_keyword_matcher, Element::Keyword)(input)
|
||||
}
|
||||
}
|
||||
}?;
|
||||
@@ -124,7 +128,7 @@ fn _detect_element<'r, 's>(
|
||||
input: OrgSource<'s>,
|
||||
can_be_paragraph: bool,
|
||||
) -> Res<OrgSource<'s>, ()> {
|
||||
if detect_plain_list(context, input).is_ok() {
|
||||
if detect_plain_list(input).is_ok() {
|
||||
return Ok((input, ()));
|
||||
}
|
||||
if _element(context, input, can_be_paragraph).is_ok() {
|
||||
|
||||
@@ -2,12 +2,12 @@ use nom::branch::alt;
|
||||
use nom::bytes::complete::tag;
|
||||
use nom::bytes::complete::tag_no_case;
|
||||
use nom::character::complete::satisfy;
|
||||
use nom::character::complete::space0;
|
||||
use nom::combinator::eof;
|
||||
use nom::combinator::peek;
|
||||
use nom::combinator::recognize;
|
||||
|
||||
use super::org_source::OrgSource;
|
||||
use super::util::maybe_consume_object_trailing_whitespace_if_not_exiting;
|
||||
use super::Context;
|
||||
use crate::error::CustomError;
|
||||
use crate::error::MyError;
|
||||
@@ -15,7 +15,8 @@ use crate::error::Res;
|
||||
use crate::parser::object::Entity;
|
||||
use crate::parser::util::get_consumed;
|
||||
|
||||
const ENTITIES: [&'static str; 413] = [
|
||||
// TODO: Make this a user-provided variable corresponding to elisp's org-entities
|
||||
const ORG_ENTITIES: [&'static str; 413] = [
|
||||
"Agrave",
|
||||
"agrave",
|
||||
"Aacute",
|
||||
@@ -439,7 +440,8 @@ pub fn entity<'r, 's>(
|
||||
let (remaining, _) = tag("\\")(input)?;
|
||||
let (remaining, entity_name) = name(context, remaining)?;
|
||||
let (remaining, _) = alt((tag("{}"), peek(recognize(entity_end))))(remaining)?;
|
||||
let (remaining, _) = space0(remaining)?;
|
||||
let (remaining, _trailing_whitespace) =
|
||||
maybe_consume_object_trailing_whitespace_if_not_exiting(context, remaining)?;
|
||||
|
||||
let source = get_consumed(input, remaining);
|
||||
Ok((
|
||||
@@ -457,8 +459,7 @@ fn name<'r, 's>(
|
||||
input: OrgSource<'s>,
|
||||
) -> Res<OrgSource<'s>, OrgSource<'s>> {
|
||||
// TODO: This should be defined by org-entities and optionally org-entities-user
|
||||
for entity in ENTITIES {
|
||||
// foo
|
||||
for entity in ORG_ENTITIES {
|
||||
let result = tag_no_case::<_, _, CustomError<_>>(entity)(input);
|
||||
match result {
|
||||
Ok((remaining, ent)) => {
|
||||
|
||||
@@ -8,6 +8,7 @@ use nom::multi::many_till;
|
||||
use nom::sequence::tuple;
|
||||
|
||||
use super::org_source::OrgSource;
|
||||
use super::util::maybe_consume_object_trailing_whitespace_if_not_exiting;
|
||||
use super::Context;
|
||||
use crate::error::Res;
|
||||
use crate::parser::exiting::ExitClass;
|
||||
@@ -35,6 +36,8 @@ pub fn export_snippet<'r, 's>(
|
||||
parser_with_context!(contents)(&parser_context),
|
||||
)))(remaining)?;
|
||||
let (remaining, _) = tag("@@")(remaining)?;
|
||||
let (remaining, _trailing_whitespace) =
|
||||
maybe_consume_object_trailing_whitespace_if_not_exiting(context, remaining)?;
|
||||
let source = get_consumed(input, remaining);
|
||||
Ok((
|
||||
remaining,
|
||||
|
||||
@@ -80,21 +80,10 @@ fn footnote_definition_end<'r, 's>(
|
||||
context: Context<'r, 's>,
|
||||
input: OrgSource<'s>,
|
||||
) -> Res<OrgSource<'s>, OrgSource<'s>> {
|
||||
let allow_nesting_context =
|
||||
context.with_additional_node(ContextElement::Context("allow nesting footnotes"));
|
||||
let footnote_definition_matcher = parser_with_context!(footnote_definition)(
|
||||
if immediate_in_section(context, "footnote definition") {
|
||||
&allow_nesting_context
|
||||
} else {
|
||||
context
|
||||
},
|
||||
);
|
||||
let maybe_consume_trailing_whitespace_matcher =
|
||||
parser_with_context!(maybe_consume_trailing_whitespace)(context);
|
||||
let (remaining, source) = alt((
|
||||
recognize(tuple((
|
||||
maybe_consume_trailing_whitespace_matcher,
|
||||
footnote_definition_matcher,
|
||||
parser_with_context!(maybe_consume_trailing_whitespace)(context),
|
||||
detect_footnote_definition,
|
||||
))),
|
||||
recognize(tuple((
|
||||
start_of_line,
|
||||
@@ -107,6 +96,12 @@ fn footnote_definition_end<'r, 's>(
|
||||
Ok((remaining, source))
|
||||
}
|
||||
|
||||
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
|
||||
fn detect_footnote_definition<'s>(input: OrgSource<'s>) -> Res<OrgSource<'s>, ()> {
|
||||
tuple((start_of_line, tag_no_case("[fn:"), label, tag("]")))(input)?;
|
||||
Ok((input, ()))
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
use nom::branch::alt;
|
||||
use nom::bytes::complete::tag;
|
||||
use nom::bytes::complete::tag_no_case;
|
||||
use nom::character::complete::space0;
|
||||
use nom::combinator::verify;
|
||||
use nom::multi::many_till;
|
||||
|
||||
use super::org_source::BracketDepth;
|
||||
use super::org_source::OrgSource;
|
||||
use super::parser_context::ContextElement;
|
||||
use super::util::maybe_consume_object_trailing_whitespace_if_not_exiting;
|
||||
use super::Context;
|
||||
use crate::error::CustomError;
|
||||
use crate::error::MyError;
|
||||
@@ -54,7 +54,8 @@ fn anonymous_footnote<'r, 's>(
|
||||
)(remaining)?;
|
||||
let (remaining, _) = tag("]")(remaining)?;
|
||||
|
||||
let (remaining, _) = space0(remaining)?;
|
||||
let (remaining, _trailing_whitespace) =
|
||||
maybe_consume_object_trailing_whitespace_if_not_exiting(context, remaining)?;
|
||||
let source = get_consumed(input, remaining);
|
||||
Ok((
|
||||
remaining,
|
||||
@@ -89,7 +90,8 @@ fn inline_footnote<'r, 's>(
|
||||
)(remaining)?;
|
||||
let (remaining, _) = tag("]")(remaining)?;
|
||||
|
||||
let (remaining, _) = space0(remaining)?;
|
||||
let (remaining, _trailing_whitespace) =
|
||||
maybe_consume_object_trailing_whitespace_if_not_exiting(context, remaining)?;
|
||||
let source = get_consumed(input, remaining);
|
||||
Ok((
|
||||
remaining,
|
||||
@@ -103,13 +105,14 @@ fn inline_footnote<'r, 's>(
|
||||
|
||||
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
|
||||
fn footnote_reference_only<'r, 's>(
|
||||
_context: Context<'r, 's>,
|
||||
context: Context<'r, 's>,
|
||||
input: OrgSource<'s>,
|
||||
) -> Res<OrgSource<'s>, FootnoteReference<'s>> {
|
||||
let (remaining, _) = tag_no_case("[fn:")(input)?;
|
||||
let (remaining, label_contents) = label(remaining)?;
|
||||
let (remaining, _) = tag("]")(remaining)?;
|
||||
let (remaining, _) = space0(remaining)?;
|
||||
let (remaining, _trailing_whitespace) =
|
||||
maybe_consume_object_trailing_whitespace_if_not_exiting(context, remaining)?;
|
||||
let source = get_consumed(input, remaining);
|
||||
Ok((
|
||||
remaining,
|
||||
|
||||
@@ -12,6 +12,7 @@ use nom::multi::many_till;
|
||||
use nom::sequence::tuple;
|
||||
|
||||
use super::org_source::OrgSource;
|
||||
use super::util::in_section;
|
||||
use super::Context;
|
||||
use crate::error::CustomError;
|
||||
use crate::error::MyError;
|
||||
@@ -26,7 +27,6 @@ use crate::parser::source::SetSource;
|
||||
use crate::parser::util::blank_line;
|
||||
use crate::parser::util::exit_matcher_parser;
|
||||
use crate::parser::util::get_consumed;
|
||||
use crate::parser::util::immediate_in_section;
|
||||
use crate::parser::util::start_of_line;
|
||||
use crate::parser::Element;
|
||||
use crate::parser::Paragraph;
|
||||
@@ -49,11 +49,11 @@ pub fn greater_block<'r, 's>(
|
||||
}),
|
||||
))(remaining)?;
|
||||
let context_name = match Into::<&str>::into(name).to_lowercase().as_str() {
|
||||
"center" => "center block",
|
||||
"quote" => "quote block",
|
||||
_ => "greater block",
|
||||
"center" => "center block".to_owned(),
|
||||
"quote" => "quote block".to_owned(),
|
||||
name @ _ => format!("special block {}", name),
|
||||
};
|
||||
if immediate_in_section(context, context_name) {
|
||||
if in_section(context, context_name.as_str()) {
|
||||
return Err(nom::Err::Error(CustomError::MyError(MyError(
|
||||
"Cannot nest objects of the same element".into(),
|
||||
))));
|
||||
@@ -63,7 +63,7 @@ pub fn greater_block<'r, 's>(
|
||||
let (remaining, _nl) = line_ending(remaining)?;
|
||||
let parser_context = context
|
||||
.with_additional_node(ContextElement::ConsumeTrailingWhitespace(true))
|
||||
.with_additional_node(ContextElement::Context(context_name))
|
||||
.with_additional_node(ContextElement::Context(context_name.as_str()))
|
||||
.with_additional_node(ContextElement::ExitMatcherNode(ExitMatcherNode {
|
||||
class: ExitClass::Alpha,
|
||||
exit_matcher: &exit_with_name,
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
use super::element::Element;
|
||||
use super::lesser_element::TableCell;
|
||||
use super::source::Source;
|
||||
use super::Object;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct PlainList<'s> {
|
||||
@@ -13,6 +14,7 @@ pub struct PlainListItem<'s> {
|
||||
pub source: &'s str,
|
||||
pub indentation: usize,
|
||||
pub bullet: &'s str,
|
||||
pub tag: Vec<Object<'s>>,
|
||||
pub children: Vec<Element<'s>>,
|
||||
}
|
||||
|
||||
|
||||
@@ -4,7 +4,6 @@ use nom::bytes::complete::tag_no_case;
|
||||
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::opt;
|
||||
use nom::combinator::recognize;
|
||||
use nom::combinator::verify;
|
||||
@@ -12,6 +11,7 @@ use nom::multi::many_till;
|
||||
|
||||
use super::org_source::BracketDepth;
|
||||
use super::org_source::OrgSource;
|
||||
use super::util::maybe_consume_object_trailing_whitespace_if_not_exiting;
|
||||
use super::Context;
|
||||
use crate::error::CustomError;
|
||||
use crate::error::MyError;
|
||||
@@ -34,7 +34,8 @@ pub fn inline_babel_call<'r, 's>(
|
||||
let (remaining, _header1) = opt(parser_with_context!(header)(context))(remaining)?;
|
||||
let (remaining, _argument) = argument(context, remaining)?;
|
||||
let (remaining, _header2) = opt(parser_with_context!(header)(context))(remaining)?;
|
||||
let (remaining, _) = space0(remaining)?;
|
||||
let (remaining, _trailing_whitespace) =
|
||||
maybe_consume_object_trailing_whitespace_if_not_exiting(context, remaining)?;
|
||||
let source = get_consumed(input, remaining);
|
||||
Ok((
|
||||
remaining,
|
||||
|
||||
@@ -4,7 +4,6 @@ use nom::bytes::complete::tag_no_case;
|
||||
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::opt;
|
||||
use nom::combinator::recognize;
|
||||
use nom::combinator::verify;
|
||||
@@ -14,6 +13,7 @@ use tracing::span;
|
||||
|
||||
use super::org_source::BracketDepth;
|
||||
use super::org_source::OrgSource;
|
||||
use super::util::maybe_consume_object_trailing_whitespace_if_not_exiting;
|
||||
use super::Context;
|
||||
use crate::error::CustomError;
|
||||
use crate::error::MyError;
|
||||
@@ -35,7 +35,8 @@ pub fn inline_source_block<'r, 's>(
|
||||
let (remaining, _) = lang(context, remaining)?;
|
||||
let (remaining, _header1) = opt(parser_with_context!(header)(context))(remaining)?;
|
||||
let (remaining, _body) = body(context, remaining)?;
|
||||
let (remaining, _) = space0(remaining)?;
|
||||
let (remaining, _trailing_whitespace) =
|
||||
maybe_consume_object_trailing_whitespace_if_not_exiting(context, remaining)?;
|
||||
let source = get_consumed(input, remaining);
|
||||
Ok((
|
||||
remaining,
|
||||
|
||||
@@ -2,6 +2,8 @@ use nom::branch::alt;
|
||||
use nom::bytes::complete::is_not;
|
||||
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::space0;
|
||||
use nom::character::complete::space1;
|
||||
@@ -9,14 +11,24 @@ use nom::combinator::eof;
|
||||
use nom::combinator::not;
|
||||
use nom::combinator::peek;
|
||||
use nom::combinator::recognize;
|
||||
use nom::multi::many_till;
|
||||
use nom::sequence::tuple;
|
||||
|
||||
use super::org_source::BracketDepth;
|
||||
use super::org_source::OrgSource;
|
||||
use super::Context;
|
||||
use crate::error::CustomError;
|
||||
use crate::error::MyError;
|
||||
use crate::error::Res;
|
||||
use crate::parser::util::start_of_line;
|
||||
use crate::parser::Keyword;
|
||||
|
||||
const ORG_ELEMENT_AFFILIATED_KEYWORDS: [&'static str; 13] = [
|
||||
"caption", "data", "header", "headers", "label", "name", "plot", "resname", "result",
|
||||
"results", "source", "srcname", "tblname",
|
||||
];
|
||||
const ORG_ELEMENT_DUAL_KEYWORDS: [&'static str; 2] = ["caption", "results"];
|
||||
|
||||
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
|
||||
pub fn keyword<'r, 's>(
|
||||
_context: Context<'r, 's>,
|
||||
@@ -41,3 +53,111 @@ pub fn keyword<'r, 's>(
|
||||
},
|
||||
))
|
||||
}
|
||||
|
||||
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
|
||||
pub fn affiliated_keyword<'r, 's>(
|
||||
_context: Context<'r, 's>,
|
||||
input: OrgSource<'s>,
|
||||
) -> Res<OrgSource<'s>, Keyword<'s>> {
|
||||
start_of_line(input)?;
|
||||
|
||||
// TODO: When key is a member of org-element-parsed-keywords, value can contain the standard set objects, excluding footnote references.
|
||||
let (remaining, rule) = recognize(tuple((
|
||||
space0,
|
||||
tag("#+"),
|
||||
affiliated_key,
|
||||
tag(":"),
|
||||
alt((recognize(tuple((space1, is_not("\r\n")))), space0)),
|
||||
alt((line_ending, eof)),
|
||||
)))(input)?;
|
||||
Ok((
|
||||
remaining,
|
||||
Keyword {
|
||||
source: rule.into(),
|
||||
},
|
||||
))
|
||||
}
|
||||
|
||||
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
|
||||
fn affiliated_key<'s>(input: OrgSource<'s>) -> Res<OrgSource<'s>, OrgSource<'s>> {
|
||||
alt((
|
||||
recognize(tuple((dual_affiliated_key, tag("["), optval, tag("]")))),
|
||||
plain_affiliated_key,
|
||||
export_keyword,
|
||||
))(input)
|
||||
}
|
||||
|
||||
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
|
||||
fn plain_affiliated_key<'s>(input: OrgSource<'s>) -> Res<OrgSource<'s>, OrgSource<'s>> {
|
||||
for keyword in ORG_ELEMENT_AFFILIATED_KEYWORDS {
|
||||
let result = tag_no_case::<_, _, CustomError<_>>(keyword)(input);
|
||||
match result {
|
||||
Ok((remaining, ent)) => {
|
||||
return Ok((remaining, ent));
|
||||
}
|
||||
Err(_) => {}
|
||||
}
|
||||
}
|
||||
|
||||
Err(nom::Err::Error(CustomError::MyError(MyError(
|
||||
"NoKeywordKey".into(),
|
||||
))))
|
||||
}
|
||||
|
||||
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
|
||||
fn dual_affiliated_key<'s>(input: OrgSource<'s>) -> Res<OrgSource<'s>, OrgSource<'s>> {
|
||||
for keyword in ORG_ELEMENT_DUAL_KEYWORDS {
|
||||
let result = tag_no_case::<_, _, CustomError<_>>(keyword)(input);
|
||||
match result {
|
||||
Ok((remaining, ent)) => {
|
||||
return Ok((remaining, ent));
|
||||
}
|
||||
Err(_) => {}
|
||||
}
|
||||
}
|
||||
|
||||
Err(nom::Err::Error(CustomError::MyError(MyError(
|
||||
"NoKeywordKey".into(),
|
||||
))))
|
||||
}
|
||||
|
||||
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
|
||||
fn optval<'s>(input: OrgSource<'s>) -> Res<OrgSource<'s>, OrgSource<'s>> {
|
||||
recognize(many_till(
|
||||
anychar,
|
||||
peek(optval_end(input.get_bracket_depth())),
|
||||
))(input)
|
||||
}
|
||||
|
||||
const fn optval_end(
|
||||
starting_bracket_depth: BracketDepth,
|
||||
) -> impl for<'s> Fn(OrgSource<'s>) -> Res<OrgSource<'s>, OrgSource<'s>> {
|
||||
move |input: OrgSource<'_>| _optval_end(input, starting_bracket_depth)
|
||||
}
|
||||
|
||||
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
|
||||
fn _optval_end<'s>(
|
||||
input: OrgSource<'s>,
|
||||
starting_bracket_depth: BracketDepth,
|
||||
) -> Res<OrgSource<'s>, OrgSource<'s>> {
|
||||
let current_depth = input.get_bracket_depth() - starting_bracket_depth;
|
||||
if current_depth < 0 {
|
||||
// This shouldn't be possible because if depth is 0 then a closing bracket should end the opval.
|
||||
unreachable!("Exceeded optval bracket depth.")
|
||||
}
|
||||
if current_depth == 0 {
|
||||
let close_bracket = tag::<_, _, CustomError<_>>("]")(input);
|
||||
if close_bracket.is_ok() {
|
||||
return close_bracket;
|
||||
}
|
||||
}
|
||||
tag("\n")(input)
|
||||
}
|
||||
|
||||
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
|
||||
fn export_keyword<'s>(input: OrgSource<'s>) -> Res<OrgSource<'s>, OrgSource<'s>> {
|
||||
recognize(tuple((
|
||||
tag_no_case("attr_"),
|
||||
take_while1(|c: char| c.is_alphanumeric() || "-_".contains(c)),
|
||||
)))(input)
|
||||
}
|
||||
|
||||
@@ -5,7 +5,6 @@ use nom::character::complete::anychar;
|
||||
use nom::character::complete::line_ending;
|
||||
use nom::character::complete::none_of;
|
||||
use nom::character::complete::one_of;
|
||||
use nom::character::complete::space0;
|
||||
use nom::combinator::peek;
|
||||
use nom::combinator::recognize;
|
||||
use nom::combinator::verify;
|
||||
@@ -14,6 +13,7 @@ use nom::multi::many_till;
|
||||
use nom::sequence::tuple;
|
||||
|
||||
use super::org_source::OrgSource;
|
||||
use super::util::maybe_consume_object_trailing_whitespace_if_not_exiting;
|
||||
use super::Context;
|
||||
use crate::error::CustomError;
|
||||
use crate::error::MyError;
|
||||
@@ -36,7 +36,8 @@ pub fn latex_fragment<'r, 's>(
|
||||
parser_with_context!(dollar_char_fragment)(context),
|
||||
parser_with_context!(bordered_dollar_fragment)(context),
|
||||
))(input)?;
|
||||
let (remaining, _) = space0(remaining)?;
|
||||
let (remaining, _trailing_whitespace) =
|
||||
maybe_consume_object_trailing_whitespace_if_not_exiting(context, remaining)?;
|
||||
let source = get_consumed(input, remaining);
|
||||
Ok((
|
||||
remaining,
|
||||
|
||||
@@ -8,7 +8,9 @@ use nom::multi::many0;
|
||||
use nom::multi::separated_list0;
|
||||
|
||||
use super::org_source::OrgSource;
|
||||
use super::util::maybe_consume_object_trailing_whitespace_if_not_exiting;
|
||||
use super::Context;
|
||||
use crate::error::CustomError;
|
||||
use crate::error::Res;
|
||||
use crate::parser::object::OrgMacro;
|
||||
use crate::parser::parser_with_context::parser_with_context;
|
||||
@@ -24,6 +26,8 @@ pub fn org_macro<'r, 's>(
|
||||
let (remaining, macro_name) = org_macro_name(context, remaining)?;
|
||||
let (remaining, macro_args) = opt(parser_with_context!(org_macro_args)(context))(remaining)?;
|
||||
let (remaining, _) = tag("}}}")(remaining)?;
|
||||
let (remaining, _trailing_whitespace) =
|
||||
maybe_consume_object_trailing_whitespace_if_not_exiting(context, remaining)?;
|
||||
|
||||
let source = get_consumed(input, remaining);
|
||||
Ok((
|
||||
@@ -84,6 +88,11 @@ fn org_macro_arg<'r, 's>(
|
||||
}
|
||||
if next_char == '\\' {
|
||||
escaping = true;
|
||||
if peek(tag::<_, _, CustomError<_>>(")"))(new_remaining).is_ok() {
|
||||
// Special case for backslash at the end of a macro
|
||||
remaining = new_remaining;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if next_char == ',' || next_char == ')' {
|
||||
break;
|
||||
|
||||
@@ -7,6 +7,7 @@ use nom::character::complete::one_of;
|
||||
use nom::combinator::eof;
|
||||
use nom::combinator::peek;
|
||||
use nom::combinator::recognize;
|
||||
use nom::combinator::verify;
|
||||
use nom::multi::many_till;
|
||||
|
||||
use super::org_source::OrgSource;
|
||||
@@ -23,6 +24,33 @@ use crate::parser::util::exit_matcher_parser;
|
||||
use crate::parser::util::get_consumed;
|
||||
use crate::parser::util::WORD_CONSTITUENT_CHARACTERS;
|
||||
|
||||
// TODO: Make this a user-provided variable corresponding to elisp's org-link-parameters
|
||||
const ORG_LINK_PARAMETERS: [&'static str; 23] = [
|
||||
"id",
|
||||
"eww",
|
||||
"rmail",
|
||||
"mhe",
|
||||
"irc",
|
||||
"info",
|
||||
"gnus",
|
||||
"docview",
|
||||
"bibtex",
|
||||
"bbdb",
|
||||
"w3m",
|
||||
"doi",
|
||||
"file+sys",
|
||||
"file+emacs",
|
||||
"shell",
|
||||
"news",
|
||||
"mailto",
|
||||
"https",
|
||||
"http",
|
||||
"ftp",
|
||||
"help",
|
||||
"file",
|
||||
"elisp",
|
||||
];
|
||||
|
||||
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
|
||||
pub fn plain_link<'r, 's>(
|
||||
context: Context<'r, 's>,
|
||||
@@ -73,36 +101,19 @@ pub fn protocol<'r, 's>(
|
||||
input: OrgSource<'s>,
|
||||
) -> Res<OrgSource<'s>, OrgSource<'s>> {
|
||||
// TODO: This should be defined by org-link-parameters
|
||||
let (remaining, proto) = alt((
|
||||
alt((
|
||||
tag_no_case("id"),
|
||||
tag_no_case("eww"),
|
||||
tag_no_case("rmail"),
|
||||
tag_no_case("mhe"),
|
||||
tag_no_case("irc"),
|
||||
tag_no_case("info"),
|
||||
tag_no_case("gnus"),
|
||||
tag_no_case("docview"),
|
||||
tag_no_case("bibtex"),
|
||||
tag_no_case("bbdb"),
|
||||
tag_no_case("w3m"),
|
||||
)),
|
||||
alt((
|
||||
tag_no_case("doi"),
|
||||
tag_no_case("file+sys"),
|
||||
tag_no_case("file+emacs"),
|
||||
tag_no_case("shell"),
|
||||
tag_no_case("news"),
|
||||
tag_no_case("mailto"),
|
||||
tag_no_case("https"),
|
||||
tag_no_case("http"),
|
||||
tag_no_case("ftp"),
|
||||
tag_no_case("help"),
|
||||
tag_no_case("file"),
|
||||
tag_no_case("elisp"),
|
||||
)),
|
||||
))(input)?;
|
||||
Ok((remaining, proto))
|
||||
for link_parameter in ORG_LINK_PARAMETERS {
|
||||
let result = tag_no_case::<_, _, CustomError<_>>(link_parameter)(input);
|
||||
match result {
|
||||
Ok((remaining, ent)) => {
|
||||
return Ok((remaining, ent));
|
||||
}
|
||||
Err(_) => {}
|
||||
}
|
||||
}
|
||||
|
||||
Err(nom::Err::Error(CustomError::MyError(MyError(
|
||||
"NoLinkProtocol".into(),
|
||||
))))
|
||||
}
|
||||
|
||||
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
|
||||
@@ -119,7 +130,11 @@ fn path_plain<'r, 's>(
|
||||
|
||||
let exit_matcher = parser_with_context!(exit_matcher_parser)(&parser_context);
|
||||
|
||||
let (remaining, path) = recognize(many_till(anychar, peek(exit_matcher)))(input)?;
|
||||
let (remaining, path) = recognize(verify(
|
||||
many_till(anychar, peek(exit_matcher)),
|
||||
|(children, _exit_contents)| !children.is_empty(),
|
||||
))(input)?;
|
||||
|
||||
Ok((remaining, path))
|
||||
}
|
||||
|
||||
@@ -128,5 +143,10 @@ fn path_plain_end<'r, 's>(
|
||||
_context: Context<'r, 's>,
|
||||
input: OrgSource<'s>,
|
||||
) -> Res<OrgSource<'s>, OrgSource<'s>> {
|
||||
recognize(one_of(" \t\r\n()[]<>"))(input)
|
||||
recognize(many_till(
|
||||
verify(anychar, |c| {
|
||||
*c != '/' && (c.is_ascii_punctuation() || c.is_whitespace())
|
||||
}),
|
||||
one_of(" \t\r\n()[]<>"),
|
||||
))(input)
|
||||
}
|
||||
|
||||
@@ -6,19 +6,24 @@ use nom::character::complete::one_of;
|
||||
use nom::character::complete::space0;
|
||||
use nom::character::complete::space1;
|
||||
use nom::combinator::eof;
|
||||
use nom::combinator::not;
|
||||
use nom::combinator::opt;
|
||||
use nom::combinator::peek;
|
||||
use nom::combinator::recognize;
|
||||
use nom::combinator::verify;
|
||||
use nom::multi::many0;
|
||||
use nom::multi::many1;
|
||||
use nom::multi::many_till;
|
||||
use nom::sequence::tuple;
|
||||
|
||||
use super::greater_element::PlainList;
|
||||
use super::greater_element::PlainListItem;
|
||||
use super::object_parser::standard_set_object;
|
||||
use super::org_source::OrgSource;
|
||||
use super::parser_with_context::parser_with_context;
|
||||
use super::util::non_whitespace_character;
|
||||
use super::Context;
|
||||
use super::Object;
|
||||
use crate::error::CustomError;
|
||||
use crate::error::MyError;
|
||||
use crate::error::Res;
|
||||
@@ -33,10 +38,7 @@ use crate::parser::util::maybe_consume_trailing_whitespace_if_not_exiting;
|
||||
use crate::parser::util::start_of_line;
|
||||
|
||||
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
|
||||
pub fn detect_plain_list<'r, 's>(
|
||||
_context: Context<'r, 's>,
|
||||
input: OrgSource<'s>,
|
||||
) -> Res<OrgSource<'s>, ()> {
|
||||
pub fn detect_plain_list<'s>(input: OrgSource<'s>) -> Res<OrgSource<'s>, ()> {
|
||||
if verify(
|
||||
tuple((
|
||||
start_of_line,
|
||||
@@ -64,6 +66,7 @@ pub fn plain_list<'r, 's>(
|
||||
) -> Res<OrgSource<'s>, PlainList<'s>> {
|
||||
let parser_context = context
|
||||
.with_additional_node(ContextElement::Context("plain list"))
|
||||
.with_additional_node(ContextElement::ConsumeTrailingWhitespace(true))
|
||||
.with_additional_node(ContextElement::ExitMatcherNode(ExitMatcherNode {
|
||||
class: ExitClass::Beta,
|
||||
exit_matcher: &plain_list_end,
|
||||
@@ -138,25 +141,33 @@ pub fn plain_list_item<'r, 's>(
|
||||
Into::<&str>::into(bull) != "*" || indent_level > 0
|
||||
})(remaining)?;
|
||||
|
||||
// TODO: This isn't taking into account items that immediately line break and then have contents
|
||||
let maybe_contentless_item: Res<OrgSource<'_>, OrgSource<'_>> = eof(remaining);
|
||||
let (remaining, maybe_tag) = opt(tuple((
|
||||
space1,
|
||||
parser_with_context!(item_tag)(context),
|
||||
tag(" ::"),
|
||||
)))(remaining)?;
|
||||
let maybe_contentless_item: Res<OrgSource<'_>, OrgSource<'_>> =
|
||||
peek(recognize(tuple((many0(blank_line), eof))))(remaining);
|
||||
match maybe_contentless_item {
|
||||
Ok((rem, _ws)) => {
|
||||
let source = get_consumed(input, rem);
|
||||
Ok((_rem, _ws)) => {
|
||||
let (remaining, _trailing_ws) = opt(blank_line)(remaining)?;
|
||||
let source = get_consumed(input, remaining);
|
||||
return Ok((
|
||||
rem,
|
||||
remaining,
|
||||
PlainListItem {
|
||||
source: source.into(),
|
||||
indentation: indent_level,
|
||||
bullet: bull.into(),
|
||||
tag: maybe_tag
|
||||
.map(|(_ws, item_tag, _divider)| item_tag)
|
||||
.unwrap_or(Vec::new()),
|
||||
children: Vec::new(),
|
||||
},
|
||||
));
|
||||
}
|
||||
Err(_) => {}
|
||||
};
|
||||
|
||||
let (remaining, _ws) = alt((space1, line_ending))(remaining)?;
|
||||
let (remaining, _ws) = item_tag_post_gap(context, remaining)?;
|
||||
let exit_matcher = plain_list_item_end(indent_level);
|
||||
let parser_context = context
|
||||
.with_additional_node(ContextElement::ConsumeTrailingWhitespace(true))
|
||||
@@ -165,11 +176,24 @@ pub fn plain_list_item<'r, 's>(
|
||||
exit_matcher: &exit_matcher,
|
||||
}));
|
||||
|
||||
let (remaining, (children, _exit_contents)) = many_till(
|
||||
parser_with_context!(element(true))(&parser_context),
|
||||
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),
|
||||
)(remaining)?;
|
||||
|
||||
if !children.is_empty() && !context.should_consume_trailing_whitespace() {
|
||||
let final_item_context =
|
||||
parser_context.with_additional_node(ContextElement::ConsumeTrailingWhitespace(false));
|
||||
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);
|
||||
}
|
||||
|
||||
let (remaining, _trailing_ws) =
|
||||
maybe_consume_trailing_whitespace_if_not_exiting(context, remaining)?;
|
||||
|
||||
@@ -180,11 +204,26 @@ pub fn plain_list_item<'r, 's>(
|
||||
source: source.into(),
|
||||
indentation: indent_level,
|
||||
bullet: bull.into(),
|
||||
children,
|
||||
tag: maybe_tag
|
||||
.map(|(_ws, item_tag, _divider)| item_tag)
|
||||
.unwrap_or(Vec::new()),
|
||||
children: children.into_iter().map(|(_start, item)| item).collect(),
|
||||
},
|
||||
));
|
||||
}
|
||||
|
||||
fn include_input<'s, F, O>(
|
||||
mut inner: F,
|
||||
) -> impl FnMut(OrgSource<'s>) -> Res<OrgSource<'s>, (OrgSource<'s>, O)>
|
||||
where
|
||||
F: FnMut(OrgSource<'s>) -> Res<OrgSource<'s>, O>,
|
||||
{
|
||||
move |input: OrgSource<'_>| {
|
||||
let (remaining, output) = inner(input)?;
|
||||
Ok((remaining, (input, output)))
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
|
||||
fn bullet<'s>(i: OrgSource<'s>) -> Res<OrgSource<'s>, OrgSource<'s>> {
|
||||
alt((
|
||||
@@ -262,6 +301,60 @@ fn _line_indented_lte<'r, 's>(
|
||||
Ok(matched)
|
||||
}
|
||||
|
||||
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
|
||||
fn item_tag<'r, 's>(
|
||||
context: Context<'r, 's>,
|
||||
input: OrgSource<'s>,
|
||||
) -> Res<OrgSource<'s>, Vec<Object<'s>>> {
|
||||
let parser_context =
|
||||
context.with_additional_node(ContextElement::ExitMatcherNode(ExitMatcherNode {
|
||||
class: ExitClass::Gamma,
|
||||
exit_matcher: &item_tag_end,
|
||||
}));
|
||||
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),
|
||||
),
|
||||
|(children, _exit_contents)| !children.is_empty(),
|
||||
)(input)?;
|
||||
Ok((remaining, children))
|
||||
}
|
||||
|
||||
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
|
||||
fn item_tag_end<'r, 's>(
|
||||
_context: Context<'r, 's>,
|
||||
input: OrgSource<'s>,
|
||||
) -> Res<OrgSource<'s>, OrgSource<'s>> {
|
||||
recognize(alt((
|
||||
line_ending,
|
||||
tag(" :: "),
|
||||
recognize(tuple((tag(" ::"), alt((line_ending, eof))))),
|
||||
)))(input)
|
||||
}
|
||||
|
||||
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
|
||||
fn item_tag_post_gap<'r, 's>(
|
||||
context: Context<'r, 's>,
|
||||
input: OrgSource<'s>,
|
||||
) -> Res<OrgSource<'s>, OrgSource<'s>> {
|
||||
verify(
|
||||
recognize(tuple((
|
||||
alt((blank_line, space0)),
|
||||
many_till(
|
||||
blank_line,
|
||||
alt((
|
||||
peek(recognize(not(blank_line))),
|
||||
peek(recognize(tuple((many0(blank_line), eof)))),
|
||||
parser_with_context!(exit_matcher_parser)(context),
|
||||
)),
|
||||
),
|
||||
))),
|
||||
|gap| gap.len() > 0,
|
||||
)(input)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
@@ -426,24 +519,21 @@ dolar"#,
|
||||
r#"+
|
||||
"#,
|
||||
);
|
||||
let initial_context: ContextTree<'_, '_> = ContextTree::new();
|
||||
let result = detect_plain_list(&initial_context, input);
|
||||
let result = detect_plain_list(input);
|
||||
assert!(result.is_ok());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn detect_eof() {
|
||||
let input = OrgSource::new(r#"+"#);
|
||||
let initial_context: ContextTree<'_, '_> = ContextTree::new();
|
||||
let result = detect_plain_list(&initial_context, input);
|
||||
let result = detect_plain_list(input);
|
||||
assert!(result.is_ok());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn detect_no_gap() {
|
||||
let input = OrgSource::new(r#"+foo"#);
|
||||
let initial_context: ContextTree<'_, '_> = ContextTree::new();
|
||||
let result = detect_plain_list(&initial_context, input);
|
||||
let result = detect_plain_list(input);
|
||||
// Since there is no whitespace after the '+' this is a paragraph, not a plain list.
|
||||
assert!(result.is_err());
|
||||
}
|
||||
@@ -451,8 +541,7 @@ dolar"#,
|
||||
#[test]
|
||||
fn detect_with_gap() {
|
||||
let input = OrgSource::new(r#"+ foo"#);
|
||||
let initial_context: ContextTree<'_, '_> = ContextTree::new();
|
||||
let result = detect_plain_list(&initial_context, input);
|
||||
let result = detect_plain_list(input);
|
||||
assert!(result.is_ok());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,13 +2,14 @@ use nom::branch::alt;
|
||||
use nom::bytes::complete::is_not;
|
||||
use nom::bytes::complete::tag;
|
||||
use nom::bytes::complete::tag_no_case;
|
||||
use nom::character::complete::anychar;
|
||||
use nom::character::complete::line_ending;
|
||||
use nom::character::complete::space0;
|
||||
use nom::character::complete::space1;
|
||||
use nom::combinator::eof;
|
||||
use nom::combinator::map;
|
||||
use nom::combinator::opt;
|
||||
use nom::combinator::recognize;
|
||||
use nom::combinator::verify;
|
||||
use nom::multi::many_till;
|
||||
use nom::sequence::tuple;
|
||||
|
||||
@@ -23,7 +24,6 @@ use crate::parser::greater_element::PropertyDrawer;
|
||||
use crate::parser::parser_context::ContextElement;
|
||||
use crate::parser::parser_context::ExitMatcherNode;
|
||||
use crate::parser::parser_with_context::parser_with_context;
|
||||
use crate::parser::plain_text::plain_text;
|
||||
use crate::parser::util::exit_matcher_parser;
|
||||
use crate::parser::util::get_consumed;
|
||||
use crate::parser::util::immediate_in_section;
|
||||
@@ -147,11 +147,16 @@ fn node_property_name<'r, 's>(
|
||||
}));
|
||||
|
||||
let (remaining, name) = recognize(tuple((
|
||||
map(parser_with_context!(plain_text)(&parser_context), |pt| {
|
||||
pt.source
|
||||
}),
|
||||
verify(
|
||||
many_till(
|
||||
anychar,
|
||||
parser_with_context!(exit_matcher_parser)(&parser_context),
|
||||
),
|
||||
|(children, _exit_contents)| !children.is_empty(),
|
||||
),
|
||||
opt(tag("+")),
|
||||
)))(input)?;
|
||||
|
||||
Ok((remaining, name))
|
||||
}
|
||||
|
||||
|
||||
@@ -6,6 +6,7 @@ use nom::combinator::verify;
|
||||
use nom::multi::many_till;
|
||||
|
||||
use super::org_source::OrgSource;
|
||||
use super::util::maybe_consume_object_trailing_whitespace_if_not_exiting;
|
||||
use super::Context;
|
||||
use super::Object;
|
||||
use crate::error::CustomError;
|
||||
@@ -104,7 +105,8 @@ pub fn radio_target<'r, 's>(
|
||||
)(remaining)?;
|
||||
|
||||
let (remaining, _closing) = tag(">>>")(remaining)?;
|
||||
let (remaining, _trailing_whitespace) = space0(remaining)?;
|
||||
let (remaining, _trailing_whitespace) =
|
||||
maybe_consume_object_trailing_whitespace_if_not_exiting(context, remaining)?;
|
||||
let source = get_consumed(input, remaining);
|
||||
Ok((
|
||||
remaining,
|
||||
|
||||
@@ -3,13 +3,13 @@ use nom::bytes::complete::escaped;
|
||||
use nom::bytes::complete::tag;
|
||||
use nom::bytes::complete::take_till1;
|
||||
use nom::character::complete::one_of;
|
||||
use nom::character::complete::space0;
|
||||
use nom::combinator::verify;
|
||||
use nom::multi::many_till;
|
||||
|
||||
use super::org_source::OrgSource;
|
||||
use super::parser_with_context::parser_with_context;
|
||||
use super::util::get_consumed;
|
||||
use super::util::maybe_consume_object_trailing_whitespace_if_not_exiting;
|
||||
use super::Context;
|
||||
use super::Object;
|
||||
use super::RegularLink;
|
||||
@@ -39,7 +39,8 @@ pub fn regular_link_without_description<'r, 's>(
|
||||
let (remaining, _opening_bracket) = tag("[[")(input)?;
|
||||
let (remaining, _path) = pathreg(context, remaining)?;
|
||||
let (remaining, _closing_bracket) = tag("]]")(remaining)?;
|
||||
let (remaining, _trailing_whitespace) = space0(remaining)?;
|
||||
let (remaining, _trailing_whitespace) =
|
||||
maybe_consume_object_trailing_whitespace_if_not_exiting(context, remaining)?;
|
||||
let source = get_consumed(input, remaining);
|
||||
Ok((
|
||||
remaining,
|
||||
@@ -59,7 +60,8 @@ pub fn regular_link_with_description<'r, 's>(
|
||||
let (remaining, _closing_bracket) = tag("][")(remaining)?;
|
||||
let (remaining, _description) = description(context, remaining)?;
|
||||
let (remaining, _closing_bracket) = tag("]]")(remaining)?;
|
||||
let (remaining, _trailing_whitespace) = space0(remaining)?;
|
||||
let (remaining, _trailing_whitespace) =
|
||||
maybe_consume_object_trailing_whitespace_if_not_exiting(context, remaining)?;
|
||||
let source = get_consumed(input, remaining);
|
||||
Ok((
|
||||
remaining,
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
use nom::branch::alt;
|
||||
use nom::bytes::complete::tag;
|
||||
use nom::character::complete::space0;
|
||||
use nom::combinator::recognize;
|
||||
use nom::sequence::tuple;
|
||||
|
||||
use super::org_source::OrgSource;
|
||||
use super::util::maybe_consume_object_trailing_whitespace_if_not_exiting;
|
||||
use super::Context;
|
||||
use crate::error::Res;
|
||||
use crate::parser::parser_with_context::parser_with_context;
|
||||
@@ -23,12 +23,13 @@ pub fn statistics_cookie<'r, 's>(
|
||||
|
||||
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
|
||||
pub fn percent_statistics_cookie<'r, 's>(
|
||||
_context: Context<'r, 's>,
|
||||
context: Context<'r, 's>,
|
||||
input: OrgSource<'s>,
|
||||
) -> Res<OrgSource<'s>, StatisticsCookie<'s>> {
|
||||
let (remaining, source) =
|
||||
recognize(tuple((tag("["), nom::character::complete::u64, tag("%]"))))(input)?;
|
||||
let (remaining, _) = space0(remaining)?;
|
||||
let (remaining, _trailing_whitespace) =
|
||||
maybe_consume_object_trailing_whitespace_if_not_exiting(context, remaining)?;
|
||||
Ok((
|
||||
remaining,
|
||||
StatisticsCookie {
|
||||
@@ -39,7 +40,7 @@ pub fn percent_statistics_cookie<'r, 's>(
|
||||
|
||||
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
|
||||
pub fn fraction_statistics_cookie<'r, 's>(
|
||||
_context: Context<'r, 's>,
|
||||
context: Context<'r, 's>,
|
||||
input: OrgSource<'s>,
|
||||
) -> Res<OrgSource<'s>, StatisticsCookie<'s>> {
|
||||
let (remaining, source) = recognize(tuple((
|
||||
@@ -49,7 +50,8 @@ pub fn fraction_statistics_cookie<'r, 's>(
|
||||
nom::character::complete::u64,
|
||||
tag("]"),
|
||||
)))(input)?;
|
||||
let (remaining, _) = space0(remaining)?;
|
||||
let (remaining, _trailing_whitespace) =
|
||||
maybe_consume_object_trailing_whitespace_if_not_exiting(context, remaining)?;
|
||||
Ok((
|
||||
remaining,
|
||||
StatisticsCookie {
|
||||
|
||||
@@ -2,7 +2,6 @@ use nom::branch::alt;
|
||||
use nom::bytes::complete::tag;
|
||||
use nom::character::complete::anychar;
|
||||
use nom::character::complete::one_of;
|
||||
use nom::character::complete::space0;
|
||||
use nom::combinator::map;
|
||||
use nom::combinator::not;
|
||||
use nom::combinator::opt;
|
||||
@@ -13,6 +12,7 @@ use nom::multi::many_till;
|
||||
|
||||
use super::org_source::BracketDepth;
|
||||
use super::org_source::OrgSource;
|
||||
use super::util::maybe_consume_object_trailing_whitespace_if_not_exiting;
|
||||
use super::Context;
|
||||
use super::Object;
|
||||
use crate::error::CustomError;
|
||||
@@ -37,7 +37,8 @@ pub fn subscript<'r, 's>(
|
||||
let (remaining, _) = tag("_")(input)?;
|
||||
pre(context, input)?;
|
||||
let (remaining, _body) = script_body(context, remaining)?;
|
||||
let (remaining, _) = space0(remaining)?;
|
||||
let (remaining, _trailing_whitespace) =
|
||||
maybe_consume_object_trailing_whitespace_if_not_exiting(context, remaining)?;
|
||||
let source = get_consumed(input, remaining);
|
||||
Ok((
|
||||
remaining,
|
||||
@@ -56,7 +57,8 @@ pub fn superscript<'r, 's>(
|
||||
let (remaining, _) = tag("^")(input)?;
|
||||
pre(context, input)?;
|
||||
let (remaining, _body) = script_body(context, remaining)?;
|
||||
let (remaining, _) = space0(remaining)?;
|
||||
let (remaining, _trailing_whitespace) =
|
||||
maybe_consume_object_trailing_whitespace_if_not_exiting(context, remaining)?;
|
||||
let source = get_consumed(input, remaining);
|
||||
Ok((
|
||||
remaining,
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
use nom::bytes::complete::tag;
|
||||
use nom::character::complete::anychar;
|
||||
use nom::character::complete::one_of;
|
||||
use nom::character::complete::space0;
|
||||
use nom::combinator::peek;
|
||||
use nom::combinator::recognize;
|
||||
use nom::combinator::verify;
|
||||
use nom::multi::many_till;
|
||||
|
||||
use super::org_source::OrgSource;
|
||||
use super::util::maybe_consume_object_trailing_whitespace_if_not_exiting;
|
||||
use super::Context;
|
||||
use crate::error::CustomError;
|
||||
use crate::error::MyError;
|
||||
@@ -49,7 +49,8 @@ pub fn target<'r, 's>(
|
||||
))));
|
||||
}
|
||||
let (remaining, _) = tag(">>")(remaining)?;
|
||||
let (remaining, _) = space0(remaining)?;
|
||||
let (remaining, _trailing_whitespace) =
|
||||
maybe_consume_object_trailing_whitespace_if_not_exiting(context, remaining)?;
|
||||
let source = get_consumed(input, remaining);
|
||||
|
||||
Ok((
|
||||
|
||||
@@ -17,6 +17,7 @@ use tracing::span;
|
||||
|
||||
use super::org_source::OrgSource;
|
||||
use super::radio_link::RematchObject;
|
||||
use super::util::maybe_consume_object_trailing_whitespace_if_not_exiting;
|
||||
use super::Context;
|
||||
use crate::error::CustomError;
|
||||
use crate::error::MyError;
|
||||
@@ -204,7 +205,8 @@ fn _text_markup_object<'r, 's, 'x>(
|
||||
}
|
||||
|
||||
let (remaining, _close) = text_markup_end_specialized(context, remaining)?;
|
||||
let (remaining, _trailing_whitespace) = space0(remaining)?;
|
||||
let (remaining, _trailing_whitespace) =
|
||||
maybe_consume_object_trailing_whitespace_if_not_exiting(context, remaining)?;
|
||||
Ok((remaining, children))
|
||||
}
|
||||
|
||||
@@ -254,7 +256,8 @@ fn _text_markup_string<'r, 's, 'x>(
|
||||
}
|
||||
|
||||
let (remaining, _close) = text_markup_end_specialized(context, remaining)?;
|
||||
let (remaining, _trailing_whitespace) = space0(remaining)?;
|
||||
let (remaining, _trailing_whitespace) =
|
||||
maybe_consume_object_trailing_whitespace_if_not_exiting(context, remaining)?;
|
||||
Ok((remaining, contents))
|
||||
}
|
||||
|
||||
@@ -277,7 +280,7 @@ pub fn pre<'r, 's>(_context: Context<'r, 's>, input: OrgSource<'s>) -> Res<OrgSo
|
||||
|
||||
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
|
||||
pub fn post<'r, 's>(_context: Context<'r, 's>, input: OrgSource<'s>) -> Res<OrgSource<'s>, ()> {
|
||||
let (remaining, _) = alt((recognize(one_of(" \r\n\t-.,;:!?')}[\">")), line_ending))(input)?;
|
||||
let (remaining, _) = alt((recognize(one_of(" \r\n\t-.,;:!?')}[\"")), line_ending))(input)?;
|
||||
Ok((remaining, ()))
|
||||
}
|
||||
|
||||
|
||||
@@ -3,7 +3,6 @@ use nom::bytes::complete::tag;
|
||||
use nom::character::complete::anychar;
|
||||
use nom::character::complete::digit1;
|
||||
use nom::character::complete::one_of;
|
||||
use nom::character::complete::space0;
|
||||
use nom::character::complete::space1;
|
||||
use nom::combinator::opt;
|
||||
use nom::combinator::recognize;
|
||||
@@ -12,6 +11,7 @@ use nom::multi::many_till;
|
||||
use nom::sequence::tuple;
|
||||
|
||||
use super::org_source::OrgSource;
|
||||
use super::util::maybe_consume_object_trailing_whitespace_if_not_exiting;
|
||||
use super::Context;
|
||||
use crate::error::Res;
|
||||
use crate::parser::exiting::ExitClass;
|
||||
@@ -49,7 +49,8 @@ fn diary_timestamp<'r, 's>(
|
||||
let (remaining, _) = tag("<%%(")(input)?;
|
||||
let (remaining, _body) = sexp(context, remaining)?;
|
||||
let (remaining, _) = tag(")>")(remaining)?;
|
||||
let (remaining, _) = space0(remaining)?;
|
||||
let (remaining, _trailing_whitespace) =
|
||||
maybe_consume_object_trailing_whitespace_if_not_exiting(context, remaining)?;
|
||||
let source = get_consumed(input, remaining);
|
||||
|
||||
Ok((
|
||||
@@ -112,7 +113,8 @@ fn active_timestamp<'r, 's>(
|
||||
)))(remaining)?;
|
||||
let (remaining, _) = tag(">")(remaining)?;
|
||||
|
||||
let (remaining, _) = space0(remaining)?;
|
||||
let (remaining, _trailing_whitespace) =
|
||||
maybe_consume_object_trailing_whitespace_if_not_exiting(context, remaining)?;
|
||||
let source = get_consumed(input, remaining);
|
||||
|
||||
Ok((
|
||||
@@ -145,7 +147,8 @@ fn inactive_timestamp<'r, 's>(
|
||||
)))(remaining)?;
|
||||
let (remaining, _) = tag("]")(remaining)?;
|
||||
|
||||
let (remaining, _) = space0(remaining)?;
|
||||
let (remaining, _trailing_whitespace) =
|
||||
maybe_consume_object_trailing_whitespace_if_not_exiting(context, remaining)?;
|
||||
let source = get_consumed(input, remaining);
|
||||
|
||||
Ok((
|
||||
@@ -166,7 +169,8 @@ fn active_date_range_timestamp<'r, 's>(
|
||||
let (remaining, _separator) = tag("--")(remaining)?;
|
||||
let (remaining, _second_timestamp) = active_timestamp(context, remaining)?;
|
||||
|
||||
let (remaining, _) = space0(remaining)?;
|
||||
let (remaining, _trailing_whitespace) =
|
||||
maybe_consume_object_trailing_whitespace_if_not_exiting(context, remaining)?;
|
||||
let source = get_consumed(input, remaining);
|
||||
|
||||
Ok((
|
||||
@@ -206,7 +210,8 @@ fn active_time_range_timestamp<'r, 's>(
|
||||
)))(remaining)?;
|
||||
let (remaining, _) = tag(">")(remaining)?;
|
||||
|
||||
let (remaining, _) = space0(remaining)?;
|
||||
let (remaining, _trailing_whitespace) =
|
||||
maybe_consume_object_trailing_whitespace_if_not_exiting(context, remaining)?;
|
||||
let source = get_consumed(input, remaining);
|
||||
|
||||
Ok((
|
||||
@@ -227,7 +232,8 @@ fn inactive_date_range_timestamp<'r, 's>(
|
||||
let (remaining, _separator) = tag("--")(remaining)?;
|
||||
let (remaining, _second_timestamp) = inactive_timestamp(context, remaining)?;
|
||||
|
||||
let (remaining, _) = space0(remaining)?;
|
||||
let (remaining, _trailing_whitespace) =
|
||||
maybe_consume_object_trailing_whitespace_if_not_exiting(context, remaining)?;
|
||||
let source = get_consumed(input, remaining);
|
||||
|
||||
Ok((
|
||||
@@ -267,7 +273,8 @@ fn inactive_time_range_timestamp<'r, 's>(
|
||||
)))(remaining)?;
|
||||
let (remaining, _) = tag("]")(remaining)?;
|
||||
|
||||
let (remaining, _) = space0(remaining)?;
|
||||
let (remaining, _trailing_whitespace) =
|
||||
maybe_consume_object_trailing_whitespace_if_not_exiting(context, remaining)?;
|
||||
let source = get_consumed(input, remaining);
|
||||
|
||||
Ok((
|
||||
|
||||
@@ -68,6 +68,18 @@ pub fn element_trailing_whitespace<'s>(input: OrgSource<'s>) -> Res<OrgSource<'s
|
||||
alt((eof, recognize(many0(blank_line))))(input)
|
||||
}
|
||||
|
||||
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
|
||||
pub fn maybe_consume_object_trailing_whitespace_if_not_exiting<'r, 's>(
|
||||
context: Context<'r, 's>,
|
||||
input: OrgSource<'s>,
|
||||
) -> Res<OrgSource<'s>, Option<OrgSource<'s>>> {
|
||||
if exit_matcher_parser(context, input).is_err() {
|
||||
opt(space0)(input)
|
||||
} else {
|
||||
Ok((input, None))
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
|
||||
pub fn maybe_consume_trailing_whitespace_if_not_exiting<'r, 's>(
|
||||
context: Context<'r, 's>,
|
||||
|
||||
@@ -1 +1,2 @@
|
||||
#[cfg(feature = "compare")]
|
||||
include!(concat!(env!("OUT_DIR"), "/tests.rs"));
|
||||
|
||||
Reference in New Issue
Block a user