Compare commits
243 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
068864ea87 | ||
|
|
03a3ddbd63 | ||
|
|
122adee23b | ||
|
|
556afecbb8 | ||
|
|
e4407cbdd1 | ||
|
|
f57d60dab0 | ||
|
|
0aa3939a75 | ||
|
|
52cb81e75e | ||
|
|
945121202d | ||
|
|
f4e0dddd9d | ||
|
|
6b62176fd0 | ||
|
|
44483b4d54 | ||
|
|
48d3de77fe | ||
|
|
680b176501 | ||
|
|
dc0338e978 | ||
|
|
ff3e0a50af | ||
|
|
03c8c07fe0 | ||
|
|
3a6fc5b669 | ||
|
|
d258cdb839 | ||
|
|
aa5629354e | ||
|
|
efc4a04829 | ||
|
|
dd611ea64a | ||
|
|
4bd5f3bec7 | ||
|
|
c2b3509b6a | ||
|
|
7f3f5fb889 | ||
|
|
e0fbf17226 | ||
|
|
4e18cbafba | ||
|
|
46c36d7f3e | ||
|
|
c46a935cfc | ||
|
|
f50415cb32 | ||
|
|
4f1a151e97 | ||
|
|
c8e3fdba51 | ||
|
|
4b3fc20c62 | ||
|
|
3131f8ac64 | ||
|
|
60a4835590 | ||
|
|
172d72aa46 | ||
|
|
b4fcc6500b | ||
|
|
ddb6f31562 | ||
|
|
dc080b30fc | ||
|
|
9901e17437 | ||
|
|
ea000894f0 | ||
|
|
e7742b529a | ||
|
|
8eba0c4923 | ||
|
|
e0c0070a13 | ||
|
|
65ce116998 | ||
|
|
e348e7d4e3 | ||
|
|
492090470c | ||
|
|
3ec900c8df | ||
|
|
d0a008ed22 | ||
|
|
f2292f1c07 | ||
|
|
44392cfcca | ||
|
|
110630d230 | ||
|
|
ebe12d96c1 | ||
|
|
24c8ac8e21 | ||
|
|
259ad6e242 | ||
|
|
dd1f7c7777 | ||
|
|
c1b471208d | ||
|
|
606bab9e6d | ||
|
|
0edf5620a2 | ||
|
|
cdf87641c5 | ||
|
|
eb2995dd3b | ||
|
|
cd6a64c015 | ||
|
|
a4a83d047d | ||
|
|
a4414369ce | ||
|
|
83e4b72307 | ||
|
|
34b3e4fa7b | ||
|
|
c0e879dc1e | ||
|
|
fa31b001f4 | ||
|
|
0897061ff6 | ||
|
|
28a3e1bc7b | ||
|
|
3fd3d20722 | ||
|
|
90735586b5 | ||
|
|
78befc7665 | ||
|
|
ef549d3b19 | ||
|
|
777c756a7f | ||
|
|
037caf369c | ||
|
|
54085b5833 | ||
|
|
2bfa8e59e7 | ||
|
|
5d31db39a4 | ||
|
|
adcd0de7e4 | ||
|
|
c2f9789a64 | ||
|
|
579cbb5d11 | ||
|
|
cad2be43bf | ||
|
|
a0a4f0eb90 | ||
|
|
9f4f8e79ce | ||
|
|
77e0dbb42e | ||
|
|
eff5cdbf40 | ||
|
|
eef3571299 | ||
|
|
f227d8405e | ||
|
|
9520e5814b | ||
|
|
28ad4fd046 | ||
|
|
7626a69fa1 | ||
|
|
121c0ce516 | ||
|
|
5a64db98fe | ||
|
|
abfae9c6c0 | ||
|
|
5272e2f1b4 | ||
|
|
90d4b11922 | ||
|
|
d552ef6569 | ||
|
|
f050e9b6a8 | ||
|
|
a5e108bc37 | ||
|
|
58290515b5 | ||
|
|
423f65046e | ||
|
|
badeaf8246 | ||
|
|
d38100581c | ||
|
|
f4eff5ca56 | ||
|
|
5b02c21ebf | ||
|
|
5f1668702a | ||
|
|
1faaeeebf1 | ||
|
|
20a7c89084 | ||
|
|
e83417b243 | ||
|
|
36b80dc093 | ||
|
|
1812b1a56e | ||
|
|
1a70b3d2c0 | ||
|
|
abf066701e | ||
|
|
4984ea4179 | ||
|
|
3cb251ea6c | ||
|
|
4bfea41291 | ||
|
|
99376515ef | ||
|
|
23f4ba4205 | ||
|
|
55ad136283 | ||
|
|
c717541099 | ||
|
|
c2e921c2dc | ||
|
|
e499169f0e | ||
|
|
84c088df67 | ||
|
|
f210f95f99 | ||
|
|
17b81c7c72 | ||
|
|
2911fce7cc | ||
|
|
e622d9fa6b | ||
|
|
8186fbb8b3 | ||
|
|
68ccff74fa | ||
|
|
9a13cb72c6 | ||
|
|
65abaa332f | ||
|
|
67e5829fd9 | ||
|
|
995b41e697 | ||
|
|
eb51bdfe2f | ||
|
|
bbb9ec637a | ||
|
|
dc012b49f5 | ||
|
|
13863a68f7 | ||
|
|
2962f76c81 | ||
|
|
b9b3ef6e74 | ||
|
|
310ab2eab2 | ||
|
|
53320070da | ||
|
|
2d5593681f | ||
|
|
b3f97dbb40 | ||
|
|
a48d76321e | ||
|
|
59222c58b1 | ||
|
|
4d95a7f244 | ||
|
|
5a8159eed7 | ||
|
|
e24fcb9ded | ||
|
|
4b94dc60d2 | ||
|
|
2046603d01 | ||
|
|
30412361e1 | ||
|
|
e846c85188 | ||
|
|
99b74095e6 | ||
|
|
6b802d36bf | ||
|
|
33ca43ca40 | ||
|
|
f5280a3090 | ||
|
|
c28d8ccea4 | ||
|
|
9690545901 | ||
|
|
eba4fb94cf | ||
|
|
565978225a | ||
|
|
cce9ca87fa | ||
|
|
683c523ece | ||
|
|
7a4dc20dc9 | ||
|
|
022dda06eb | ||
|
|
7b88a2d248 | ||
|
|
fce5b92091 | ||
|
|
45a506334c | ||
|
|
e47901a67f | ||
|
|
7430daa768 | ||
|
|
6ce25c8a3b | ||
|
|
7b8fa1eb4a | ||
|
|
ffa5349f25 | ||
|
|
bb472b63cc | ||
|
|
57f566a7a1 | ||
|
|
2181993246 | ||
|
|
60d1ecfa75 | ||
|
|
3962db12a8 | ||
|
|
f192507cd9 | ||
|
|
252be3e001 | ||
|
|
28f12a04f7 | ||
|
|
d6232dc49c | ||
|
|
68a220aa1c | ||
|
|
2e7db0f8bd | ||
|
|
175ff1e6c4 | ||
|
|
0b42139393 | ||
|
|
67a9103b07 | ||
|
|
f141a4e186 | ||
|
|
aba29df34c | ||
|
|
87ce7d7432 | ||
|
|
68dccd54b1 | ||
|
|
4753f4c7c6 | ||
|
|
13c62bf29f | ||
|
|
670209e9fc | ||
|
|
4af0d3141f | ||
|
|
ab281de3c6 | ||
|
|
d556d28f49 | ||
|
|
9cfb2fa052 | ||
|
|
30c03b5529 | ||
|
|
b943f90766 | ||
|
|
0108f5b0b1 | ||
|
|
50145c6cf2 | ||
|
|
4a8607726c | ||
|
|
9bcba4020d | ||
|
|
8fd9ff3848 | ||
|
|
3fb7cb82cd | ||
|
|
e0ec5c115f | ||
|
|
f0868ba3ed | ||
|
|
425bc12353 | ||
|
|
03754be71e | ||
|
|
70002800c2 | ||
|
|
281c35677b | ||
|
|
92d15c3d91 | ||
|
|
b1773ac90e | ||
|
|
645d9abf9c | ||
|
|
d2f2bdf88d | ||
|
|
90ba17b68c | ||
|
|
31406fd520 | ||
|
|
49bc51ba89 | ||
|
|
92592104a4 | ||
|
|
33f4614d28 | ||
|
|
6c197c376a | ||
|
|
bcf1b49db2 | ||
|
|
49f6e70a19 | ||
|
|
31fb815681 | ||
|
|
7dfe24ff98 | ||
|
|
a5627d0cee | ||
|
|
93cfa71df2 | ||
|
|
78320d3265 | ||
|
|
9e908935f8 | ||
|
|
b18a703529 | ||
|
|
ea52dc60be | ||
|
|
f5699ce830 | ||
|
|
10aa0956ee | ||
|
|
816c164996 | ||
|
|
ee201e1336 | ||
|
|
4897952330 | ||
|
|
e1d85c6dc2 | ||
|
|
c420ccd029 | ||
|
|
a880629831 | ||
|
|
5e2dea1f28 | ||
|
|
f47d688be4 | ||
|
|
acfc5e5e68 |
@@ -192,6 +192,54 @@ spec:
|
|||||||
]
|
]
|
||||||
- name: docker-image
|
- name: docker-image
|
||||||
value: "$(params.image-name):$(tasks.fetch-repository.results.commit)"
|
value: "$(params.image-name):$(tasks.fetch-repository.results.commit)"
|
||||||
|
- name: run-image-wasm
|
||||||
|
taskRef:
|
||||||
|
name: run-docker-image
|
||||||
|
workspaces:
|
||||||
|
- name: source
|
||||||
|
workspace: git-source
|
||||||
|
- name: cargo-cache
|
||||||
|
workspace: cargo-cache
|
||||||
|
runAfter:
|
||||||
|
- run-image-all
|
||||||
|
params:
|
||||||
|
- name: args
|
||||||
|
value:
|
||||||
|
[
|
||||||
|
"--target",
|
||||||
|
"wasm32-unknown-unknown",
|
||||||
|
"--profile",
|
||||||
|
"wasm",
|
||||||
|
"--bin",
|
||||||
|
"wasm",
|
||||||
|
"--no-default-features",
|
||||||
|
"--features",
|
||||||
|
"wasm",
|
||||||
|
]
|
||||||
|
- name: docker-image
|
||||||
|
value: "$(params.image-name):$(tasks.fetch-repository.results.commit)"
|
||||||
|
- name: run-image-wasm-test
|
||||||
|
taskRef:
|
||||||
|
name: run-docker-image
|
||||||
|
workspaces:
|
||||||
|
- name: source
|
||||||
|
workspace: git-source
|
||||||
|
- name: cargo-cache
|
||||||
|
workspace: cargo-cache
|
||||||
|
runAfter:
|
||||||
|
- run-image-wasm
|
||||||
|
params:
|
||||||
|
- name: args
|
||||||
|
value:
|
||||||
|
[
|
||||||
|
"--bin",
|
||||||
|
"wasm_test",
|
||||||
|
"--no-default-features",
|
||||||
|
"--features",
|
||||||
|
"wasm_test",
|
||||||
|
]
|
||||||
|
- name: docker-image
|
||||||
|
value: "$(params.image-name):$(tasks.fetch-repository.results.commit)"
|
||||||
finally:
|
finally:
|
||||||
- name: report-success
|
- name: report-success
|
||||||
when:
|
when:
|
||||||
|
|||||||
@@ -115,7 +115,7 @@ spec:
|
|||||||
[
|
[
|
||||||
--no-default-features,
|
--no-default-features,
|
||||||
--features,
|
--features,
|
||||||
compare,
|
"compare,wasm_test",
|
||||||
--no-fail-fast,
|
--no-fail-fast,
|
||||||
--lib,
|
--lib,
|
||||||
--test,
|
--test,
|
||||||
|
|||||||
@@ -127,7 +127,7 @@ spec:
|
|||||||
- name: command
|
- name: command
|
||||||
value: ["cargo", "fix"]
|
value: ["cargo", "fix"]
|
||||||
- name: args
|
- name: args
|
||||||
value: ["--allow-dirty"]
|
value: ["--all-targets", "--all-features", "--allow-dirty"]
|
||||||
- name: docker-image
|
- name: docker-image
|
||||||
value: "$(params.image-name):$(tasks.fetch-repository.results.commit)"
|
value: "$(params.image-name):$(tasks.fetch-repository.results.commit)"
|
||||||
- name: commit-changes
|
- name: commit-changes
|
||||||
|
|||||||
24
Cargo.toml
24
Cargo.toml
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
[package]
|
[package]
|
||||||
name = "organic"
|
name = "organic"
|
||||||
version = "0.1.12"
|
version = "0.1.14"
|
||||||
authors = ["Tom Alexander <tom@fizz.buzz>"]
|
authors = ["Tom Alexander <tom@fizz.buzz>"]
|
||||||
description = "An org-mode parser."
|
description = "An org-mode parser."
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
@@ -39,17 +39,32 @@ path = "src/lib.rs"
|
|||||||
path = "src/bin_foreign_document_test.rs"
|
path = "src/bin_foreign_document_test.rs"
|
||||||
required-features = ["foreign_document_test"]
|
required-features = ["foreign_document_test"]
|
||||||
|
|
||||||
|
[[bin]]
|
||||||
|
name = "wasm"
|
||||||
|
path = "src/bin_wasm.rs"
|
||||||
|
required-features = ["wasm"]
|
||||||
|
|
||||||
|
[[bin]]
|
||||||
|
# This bin exists for development purposes only. The real target of this crate is the library.
|
||||||
|
name = "wasm_test"
|
||||||
|
path = "src/bin_wasm_test.rs"
|
||||||
|
required-features = ["wasm_test"]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
futures = { version = "0.3.28", optional = true }
|
futures = { version = "0.3.28", optional = true }
|
||||||
nom = "7.1.1"
|
nom = "7.1.1"
|
||||||
opentelemetry = { version = "0.20.0", optional = true, default-features = false, features = ["trace", "rt-tokio"] }
|
opentelemetry = { version = "0.20.0", optional = true, default-features = false, features = ["trace", "rt-tokio"] }
|
||||||
opentelemetry-otlp = { version = "0.13.0", optional = true }
|
opentelemetry-otlp = { version = "0.13.0", optional = true }
|
||||||
opentelemetry-semantic-conventions = { version = "0.12.0", optional = true }
|
opentelemetry-semantic-conventions = { version = "0.12.0", optional = true }
|
||||||
|
serde = { version = "1.0.193", optional = true, features = ["derive"] }
|
||||||
|
serde-wasm-bindgen = { version = "0.6.3", optional = true }
|
||||||
|
serde_json = { version = "1.0.108", optional = true }
|
||||||
tokio = { version = "1.30.0", optional = true, default-features = false, features = ["rt", "rt-multi-thread"] }
|
tokio = { version = "1.30.0", optional = true, default-features = false, features = ["rt", "rt-multi-thread"] }
|
||||||
tracing = { version = "0.1.37", optional = true }
|
tracing = { version = "0.1.37", optional = true }
|
||||||
tracing-opentelemetry = { version = "0.20.0", optional = true }
|
tracing-opentelemetry = { version = "0.20.0", optional = true }
|
||||||
tracing-subscriber = { version = "0.3.17", optional = true, features = ["env-filter"] }
|
tracing-subscriber = { version = "0.3.17", optional = true, features = ["env-filter"] }
|
||||||
walkdir = { version = "2.3.3", optional = true }
|
walkdir = { version = "2.3.3", optional = true }
|
||||||
|
wasm-bindgen = { version = "0.2.89", optional = true }
|
||||||
|
|
||||||
[build-dependencies]
|
[build-dependencies]
|
||||||
walkdir = "2.3.3"
|
walkdir = "2.3.3"
|
||||||
@@ -60,6 +75,8 @@ compare = ["tokio/process", "tokio/macros"]
|
|||||||
foreign_document_test = ["compare", "dep:futures", "tokio/sync", "dep:walkdir", "tokio/process"]
|
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"]
|
tracing = ["dep:opentelemetry", "dep:opentelemetry-otlp", "dep:opentelemetry-semantic-conventions", "dep:tokio", "dep:tracing", "dep:tracing-opentelemetry", "dep:tracing-subscriber"]
|
||||||
event_count = []
|
event_count = []
|
||||||
|
wasm = ["dep:serde", "dep:wasm-bindgen", "dep:serde-wasm-bindgen"]
|
||||||
|
wasm_test = ["wasm", "dep:serde_json", "tokio/process", "tokio/macros"]
|
||||||
|
|
||||||
# Optimized build for any sort of release.
|
# Optimized build for any sort of release.
|
||||||
[profile.release-lto]
|
[profile.release-lto]
|
||||||
@@ -79,3 +96,8 @@ strip = "symbols"
|
|||||||
inherits = "release"
|
inherits = "release"
|
||||||
lto = true
|
lto = true
|
||||||
debug = true
|
debug = true
|
||||||
|
|
||||||
|
[profile.wasm]
|
||||||
|
inherits = "release"
|
||||||
|
lto = true
|
||||||
|
strip = true
|
||||||
|
|||||||
20
Makefile
20
Makefile
@@ -29,6 +29,11 @@ build:
|
|||||||
release:
|
release:
|
||||||
> cargo build --release $(RELEASEFLAGS)
|
> cargo build --release $(RELEASEFLAGS)
|
||||||
|
|
||||||
|
.PHONY: wasm
|
||||||
|
wasm:
|
||||||
|
> cargo build --target=wasm32-unknown-unknown --profile wasm --bin wasm --features wasm
|
||||||
|
> wasm-bindgen --target web --out-dir target/wasm32-unknown-unknown/js target/wasm32-unknown-unknown/wasm/wasm.wasm
|
||||||
|
|
||||||
.PHONY: clean
|
.PHONY: clean
|
||||||
clean:
|
clean:
|
||||||
> cargo clean
|
> cargo clean
|
||||||
@@ -45,19 +50,24 @@ dockerclippy:
|
|||||||
clippy:
|
clippy:
|
||||||
> cargo clippy --no-deps --all-targets --all-features -- -D warnings
|
> cargo clippy --no-deps --all-targets --all-features -- -D warnings
|
||||||
|
|
||||||
.PHONY: clippyfix
|
|
||||||
clippyfix:
|
|
||||||
> cargo clippy --fix --lib -p organic --all-features
|
|
||||||
|
|
||||||
.PHONY: test
|
.PHONY: test
|
||||||
test:
|
test:
|
||||||
> cargo test --no-default-features --features compare --no-fail-fast --lib --test test_loader -- --test-threads $(TESTJOBS)
|
> cargo test --no-default-features --features compare --no-fail-fast --lib --test test_loader -- --test-threads $(TESTJOBS)
|
||||||
|
|
||||||
|
.PHONY: doc
|
||||||
|
doc:
|
||||||
|
> cargo doc --no-deps --open --lib --release --all-features
|
||||||
|
|
||||||
.PHONY: dockertest
|
.PHONY: dockertest
|
||||||
dockertest:
|
dockertest:
|
||||||
> $(MAKE) -C docker/organic_test
|
> $(MAKE) -C docker/organic_test
|
||||||
> 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)
|
> 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: dockerwasmtest
|
||||||
|
dockerwasmtest:
|
||||||
|
> $(MAKE) -C docker/organic_test
|
||||||
|
> 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,wasm_test --no-fail-fast --lib --test test_loader autogen_wasm_ -- --test-threads $(TESTJOBS)
|
||||||
|
|
||||||
.PHONY: buildtest
|
.PHONY: buildtest
|
||||||
buildtest:
|
buildtest:
|
||||||
> cargo build --no-default-features
|
> cargo build --no-default-features
|
||||||
@@ -66,6 +76,8 @@ buildtest:
|
|||||||
> cargo build --no-default-features --features compare,tracing
|
> cargo build --no-default-features --features compare,tracing
|
||||||
> cargo build --no-default-features --features compare,foreign_document_test
|
> cargo build --no-default-features --features compare,foreign_document_test
|
||||||
> cargo build --no-default-features --features compare,tracing,foreign_document_test
|
> cargo build --no-default-features --features compare,tracing,foreign_document_test
|
||||||
|
> cargo build --target wasm32-unknown-unknown --profile wasm --bin wasm --no-default-features --features wasm
|
||||||
|
> cargo build --bin wasm_test --no-default-features --features wasm_test
|
||||||
|
|
||||||
.PHONY: foreign_document_test
|
.PHONY: foreign_document_test
|
||||||
foreign_document_test:
|
foreign_document_test:
|
||||||
|
|||||||
@@ -2,5 +2,6 @@ FROM rustlang/rust:nightly-alpine3.17
|
|||||||
|
|
||||||
RUN apk add --no-cache musl-dev
|
RUN apk add --no-cache musl-dev
|
||||||
RUN cargo install --locked --no-default-features --features ci-autoclean cargo-cache
|
RUN cargo install --locked --no-default-features --features ci-autoclean cargo-cache
|
||||||
|
RUN rustup target add wasm32-unknown-unknown
|
||||||
|
|
||||||
ENTRYPOINT ["cargo", "build"]
|
ENTRYPOINT ["cargo", "build"]
|
||||||
|
|||||||
@@ -25,13 +25,13 @@ ifdef REMOTE_REPO
|
|||||||
else
|
else
|
||||||
@echo "REMOTE_REPO not defined, not removing from remote repo."
|
@echo "REMOTE_REPO not defined, not removing from remote repo."
|
||||||
endif
|
endif
|
||||||
docker volume rm cargo-cache
|
docker volume rm rust-cache cargo-cache
|
||||||
|
|
||||||
# NOTE: This target will write to folders underneath the git-root
|
# NOTE: This target will write to folders underneath the git-root
|
||||||
.PHONY: run
|
.PHONY: run
|
||||||
run: build
|
run: build
|
||||||
docker run --rm --init --read-only --mount type=tmpfs,destination=/tmp -v "$$(readlink -f ../../):/source" --workdir=/source --mount source=cargo-cache,target=/usr/local/cargo/registry $(IMAGE_NAME)
|
docker run --rm --init -t --read-only --mount type=tmpfs,destination=/tmp -v "$$(readlink -f ../../):/source:ro" --workdir=/source --mount source=cargo-cache,target=/usr/local/cargo/registry --mount source=rust-cache,target=/target --env CARGO_TARGET_DIR=/target $(IMAGE_NAME)
|
||||||
|
|
||||||
.PHONY: shell
|
.PHONY: shell
|
||||||
shell: build
|
shell: build
|
||||||
docker run --rm -i -t --entrypoint /bin/sh --mount type=tmpfs,destination=/tmp -v "$$(readlink -f ../../):/source" --workdir=/source --mount source=cargo-cache,target=/usr/local/cargo/registry $(IMAGE_NAME)
|
docker run --rm -i -t --entrypoint /bin/sh --mount type=tmpfs,destination=/tmp -v "$$(readlink -f ../../):/source:ro" --workdir=/source --mount source=cargo-cache,target=/usr/local/cargo/registry --mount source=rust-cache,target=/target --env CARGO_TARGET_DIR=/target $(IMAGE_NAME)
|
||||||
|
|||||||
@@ -93,6 +93,12 @@ ARG WORG_PATH=/foreign_documents/worg
|
|||||||
ARG WORG_REPO=https://git.sr.ht/~bzg/worg
|
ARG WORG_REPO=https://git.sr.ht/~bzg/worg
|
||||||
RUN mkdir -p $WORG_PATH && git -C $WORG_PATH init --initial-branch=main && git -C $WORG_PATH remote add origin $WORG_REPO && git -C $WORG_PATH fetch origin $WORG_VERSION && git -C $WORG_PATH checkout FETCH_HEAD
|
RUN mkdir -p $WORG_PATH && git -C $WORG_PATH init --initial-branch=main && git -C $WORG_PATH remote add origin $WORG_REPO && git -C $WORG_PATH fetch origin $WORG_VERSION && git -C $WORG_PATH checkout FETCH_HEAD
|
||||||
|
|
||||||
|
ARG LITERATE_BUILD_EMACS_VERSION=e3ac1afe1e40af601be7af12c1d13d96308ab209
|
||||||
|
ARG LITERATE_BUILD_EMACS_PATH=/foreign_documents/literate_build_emacs
|
||||||
|
ARG LITERATE_BUILD_EMACS_REPO=https://gitlab.com/spudlyo/orgdemo2.git
|
||||||
|
RUN mkdir -p $LITERATE_BUILD_EMACS_PATH && git -C $LITERATE_BUILD_EMACS_PATH init --initial-branch=main && git -C $LITERATE_BUILD_EMACS_PATH remote add origin $LITERATE_BUILD_EMACS_REPO && git -C $LITERATE_BUILD_EMACS_PATH fetch origin $LITERATE_BUILD_EMACS_VERSION && git -C $LITERATE_BUILD_EMACS_PATH checkout FETCH_HEAD
|
||||||
|
# unused/aws.org contains invalid paths for setupfile which causes both upstream org-mode and Organic to error out.
|
||||||
|
RUN rm $LITERATE_BUILD_EMACS_PATH/unused/aws.org
|
||||||
|
|
||||||
FROM tester as foreign-document-test
|
FROM tester as foreign-document-test
|
||||||
RUN apk add --no-cache bash coreutils
|
RUN apk add --no-cache bash coreutils
|
||||||
@@ -100,6 +106,7 @@ RUN mkdir /foreign_documents
|
|||||||
COPY --from=foreign-document-gather /foreign_documents/howardabrams /foreign_documents/howardabrams
|
COPY --from=foreign-document-gather /foreign_documents/howardabrams /foreign_documents/howardabrams
|
||||||
COPY --from=foreign-document-gather /foreign_documents/doomemacs /foreign_documents/doomemacs
|
COPY --from=foreign-document-gather /foreign_documents/doomemacs /foreign_documents/doomemacs
|
||||||
COPY --from=foreign-document-gather /foreign_documents/worg /foreign_documents/worg
|
COPY --from=foreign-document-gather /foreign_documents/worg /foreign_documents/worg
|
||||||
|
COPY --from=foreign-document-gather /foreign_documents/literate_build_emacs /foreign_documents/literate_build_emacs
|
||||||
COPY --from=build-org-mode /root/org-mode /foreign_documents/org-mode
|
COPY --from=build-org-mode /root/org-mode /foreign_documents/org-mode
|
||||||
COPY --from=build-emacs /root/emacs /foreign_documents/emacs
|
COPY --from=build-emacs /root/emacs /foreign_documents/emacs
|
||||||
ENTRYPOINT ["cargo", "run", "--bin", "foreign_document_test", "--features", "compare,foreign_document_test", "--profile", "release-lto"]
|
ENTRYPOINT ["cargo", "run", "--bin", "foreign_document_test", "--features", "compare,foreign_document_test", "--profile", "release-lto"]
|
||||||
|
|||||||
5
org_mode_samples/affiliated_keyword/empty_caption.org
Normal file
5
org_mode_samples/affiliated_keyword/empty_caption.org
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
#+caption:
|
||||||
|
#+caption: *foo*
|
||||||
|
#+caption[bar]:
|
||||||
|
#+begin_src bash
|
||||||
|
#+end_src
|
||||||
0
org_mode_samples/document/empty.org
Normal file
0
org_mode_samples/document/empty.org
Normal file
4
org_mode_samples/document/only_line_breaks.org
Normal file
4
org_mode_samples/document/only_line_breaks.org
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
5
org_mode_samples/document/post_blank.org
Normal file
5
org_mode_samples/document/post_blank.org
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
* foo
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -1,3 +1,32 @@
|
|||||||
|
* Empty
|
||||||
|
:PROPERTIES:
|
||||||
|
:END:
|
||||||
|
* Single new line
|
||||||
:PROPERTIES:
|
:PROPERTIES:
|
||||||
|
|
||||||
|
:END:
|
||||||
|
* Single line with spaces
|
||||||
|
:PROPERTIES:
|
||||||
|
|
||||||
|
:END:
|
||||||
|
* Many lines, first line without spaces
|
||||||
|
:PROPERTIES:
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
:END:
|
||||||
|
* Many lines, first line with spaces
|
||||||
|
:PROPERTIES:
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
:END:
|
||||||
|
* Many lines, first line with spaces, later line with spaces
|
||||||
|
:PROPERTIES:
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
:END:
|
:END:
|
||||||
|
|||||||
@@ -5,3 +5,5 @@
|
|||||||
#+call: dolar cat(dog)
|
#+call: dolar cat(dog)
|
||||||
|
|
||||||
#+call: (bat)
|
#+call: (bat)
|
||||||
|
|
||||||
|
#+call:
|
||||||
|
|||||||
@@ -0,0 +1,3 @@
|
|||||||
|
: foo
|
||||||
|
:
|
||||||
|
: bar
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
1. foo
|
||||||
|
#+begin_src text
|
||||||
|
|
||||||
|
#+end_src
|
||||||
|
|
||||||
|
2. baz
|
||||||
@@ -1,3 +1,3 @@
|
|||||||
foo <<bar>> baz
|
<<FOO>> bar
|
||||||
|
|
||||||
lorem << ipsum >> dolar
|
[[FOO][baz]]
|
||||||
|
|||||||
@@ -0,0 +1,5 @@
|
|||||||
|
* foo
|
||||||
|
|
||||||
|
** bar
|
||||||
|
|
||||||
|
* baz
|
||||||
76
scripts/build_all_feature_flag_combinations.bash
Executable file
76
scripts/build_all_feature_flag_combinations.bash
Executable file
@@ -0,0 +1,76 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
#
|
||||||
|
# Time running a single parse without invoking a compare with emacs.
|
||||||
|
set -euo pipefail
|
||||||
|
IFS=$'\n\t'
|
||||||
|
DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
|
||||||
|
|
||||||
|
: ${PROFILE:="debug"}
|
||||||
|
|
||||||
|
############## Setup #########################
|
||||||
|
|
||||||
|
function cleanup {
|
||||||
|
for f in "${folders[@]}"; do
|
||||||
|
log "Deleting $f"
|
||||||
|
rm -rf "$f"
|
||||||
|
done
|
||||||
|
}
|
||||||
|
folders=()
|
||||||
|
for sig in EXIT INT QUIT HUP TERM; do
|
||||||
|
trap "set +e; cleanup" "$sig"
|
||||||
|
done
|
||||||
|
|
||||||
|
function die {
|
||||||
|
local status_code="$1"
|
||||||
|
shift
|
||||||
|
(>&2 echo "${@}")
|
||||||
|
exit "$status_code"
|
||||||
|
}
|
||||||
|
|
||||||
|
function log {
|
||||||
|
(>&2 echo "${@}")
|
||||||
|
}
|
||||||
|
|
||||||
|
############## Program #########################
|
||||||
|
|
||||||
|
function main {
|
||||||
|
if [ "$#" -gt 0 ]; then
|
||||||
|
export CARGO_TARGET_DIR="$1"
|
||||||
|
else
|
||||||
|
local work_directory=$(mktemp -d -t 'organic.XXXXXX')
|
||||||
|
folders+=("$work_directory")
|
||||||
|
export CARGO_TARGET_DIR="$work_directory"
|
||||||
|
fi
|
||||||
|
local features=(compare foreign_document_test tracing event_count wasm wasm_test)
|
||||||
|
ENABLED_FEATURES= for_each_combination "${features[@]}"
|
||||||
|
}
|
||||||
|
|
||||||
|
function for_each_combination {
|
||||||
|
local additional_flags=()
|
||||||
|
if [ "$PROFILE" = "dev" ] || [ "$PROFILE" = "debug" ]; then
|
||||||
|
PROFILE="debug"
|
||||||
|
else
|
||||||
|
additional_flags+=(--profile "$PROFILE")
|
||||||
|
fi
|
||||||
|
|
||||||
|
|
||||||
|
local flag=$1
|
||||||
|
shift
|
||||||
|
|
||||||
|
if [ "$#" -gt 0 ]; then
|
||||||
|
ENABLED_FEATURES="$ENABLED_FEATURES" for_each_combination "${@}"
|
||||||
|
elif [ -z "$ENABLED_FEATURES" ]; then
|
||||||
|
(cd "$DIR/../" && printf "\n\n\n========== no features ==========\n\n\n" && set -x && cargo build "${additional_flags[@]}" --no-default-features)
|
||||||
|
else
|
||||||
|
(cd "$DIR/../" && printf "\n\n\n========== %s ==========\n\n\n" "${ENABLED_FEATURES:1}" && set -x && cargo build "${additional_flags[@]}" --no-default-features --features "${ENABLED_FEATURES:1}")
|
||||||
|
fi
|
||||||
|
|
||||||
|
ENABLED_FEATURES="$ENABLED_FEATURES,$flag"
|
||||||
|
if [ "$#" -gt 0 ]; then
|
||||||
|
ENABLED_FEATURES="$ENABLED_FEATURES" for_each_combination "${@}"
|
||||||
|
else
|
||||||
|
(cd "$DIR/../" && printf "\n\n\n========== %s ==========\n\n\n" "${ENABLED_FEATURES:1}" && set -x && cargo build "${additional_flags[@]}" --no-default-features --features "${ENABLED_FEATURES:1}")
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
main "${@}"
|
||||||
111
scripts/run_docker_wasm_compare.bash
Executable file
111
scripts/run_docker_wasm_compare.bash
Executable file
@@ -0,0 +1,111 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
#
|
||||||
|
set -euo pipefail
|
||||||
|
IFS=$'\n\t'
|
||||||
|
DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
|
||||||
|
|
||||||
|
: ${SHELL:="NO"} # or YES to launch a shell instead of running the test
|
||||||
|
: ${TRACE:="NO"} # or YES to send traces to jaeger
|
||||||
|
: ${BACKTRACE:="NO"} # or YES to print a rust backtrace when panicking
|
||||||
|
: ${NO_COLOR:=""} # Set to anything to disable color output
|
||||||
|
: ${PROFILE:="debug"}
|
||||||
|
|
||||||
|
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 {
|
||||||
|
build_container
|
||||||
|
launch_container "${@}"
|
||||||
|
}
|
||||||
|
|
||||||
|
function build_container {
|
||||||
|
$MAKE -C "$DIR/../docker/organic_test"
|
||||||
|
}
|
||||||
|
|
||||||
|
function launch_container {
|
||||||
|
local additional_flags=()
|
||||||
|
local features=(wasm_test)
|
||||||
|
|
||||||
|
if [ "$NO_COLOR" != "" ]; then
|
||||||
|
additional_flags+=(--env "NO_COLOR=$NO_COLOR")
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ "$TRACE" = "YES" ]; then
|
||||||
|
# We use the host network so it can talk to jaeger hosted at 127.0.0.1
|
||||||
|
additional_flags+=(--network=host --env RUST_LOG=debug)
|
||||||
|
features+=(tracing)
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ "$SHELL" != "YES" ]; then
|
||||||
|
additional_flags+=(--read-only)
|
||||||
|
else
|
||||||
|
additional_flags+=(-t)
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ "$BACKTRACE" = "YES" ]; then
|
||||||
|
additional_flags+=(--env RUST_BACKTRACE=full)
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ "$SHELL" = "YES" ]; then
|
||||||
|
exec docker run "${additional_flags[@]}" --init --rm -i --mount type=tmpfs,destination=/tmp -v "/:/input:ro" -v "$($REALPATH "$DIR/../"):/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 /bin/sh
|
||||||
|
fi
|
||||||
|
|
||||||
|
local features_joined
|
||||||
|
features_joined=$(IFS=","; echo "${features[*]}")
|
||||||
|
|
||||||
|
local build_flags=()
|
||||||
|
if [ "$PROFILE" = "dev" ] || [ "$PROFILE" = "debug" ]; then
|
||||||
|
PROFILE="debug"
|
||||||
|
else
|
||||||
|
build_flags+=(--profile "$PROFILE")
|
||||||
|
fi
|
||||||
|
|
||||||
|
|
||||||
|
if [ $# -gt 0 ]; then
|
||||||
|
# If we passed in args, we need to forward them along
|
||||||
|
for path in "${@}"; do
|
||||||
|
local full_path
|
||||||
|
full_path=$($REALPATH "$path")
|
||||||
|
init_script=$(cat <<EOF
|
||||||
|
set -euo pipefail
|
||||||
|
IFS=\$'\n\t'
|
||||||
|
|
||||||
|
cargo build --bin wasm_test --no-default-features --features "$features_joined" ${build_flags[@]}
|
||||||
|
exec /target/${PROFILE}/wasm_test "/input${full_path}"
|
||||||
|
EOF
|
||||||
|
)
|
||||||
|
docker run "${additional_flags[@]}" --init --rm -i --mount type=tmpfs,destination=/tmp -v "/:/input:ro" -v "$($REALPATH "$DIR/../"):/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"
|
||||||
|
done
|
||||||
|
else
|
||||||
|
local current_directory init_script
|
||||||
|
current_directory=$(pwd)
|
||||||
|
init_script=$(cat <<EOF
|
||||||
|
set -euo pipefail
|
||||||
|
IFS=\$'\n\t'
|
||||||
|
|
||||||
|
cargo build --bin wasm_test --no-default-features --features "$features_joined" ${build_flags[@]}
|
||||||
|
cd /input${current_directory}
|
||||||
|
exec /target/${PROFILE}/wasm_test
|
||||||
|
EOF
|
||||||
|
)
|
||||||
|
|
||||||
|
docker run "${additional_flags[@]}" --init --rm -i --mount type=tmpfs,destination=/tmp -v "/:/input:ro" -v "$($REALPATH "$DIR/../"):/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"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
main "${@}"
|
||||||
@@ -1,3 +1,4 @@
|
|||||||
|
#![feature(exit_status_error)]
|
||||||
#![feature(round_char_boundary)]
|
#![feature(round_char_boundary)]
|
||||||
#![feature(exact_size_is_empty)]
|
#![feature(exact_size_is_empty)]
|
||||||
use std::io::Read;
|
use std::io::Read;
|
||||||
|
|||||||
@@ -53,6 +53,9 @@ async fn main_body() -> Result<ExitCode, Box<dyn std::error::Error>> {
|
|||||||
let layer = layer.chain(compare_group("doomemacs", || {
|
let layer = layer.chain(compare_group("doomemacs", || {
|
||||||
compare_all_org_document("/foreign_documents/doomemacs")
|
compare_all_org_document("/foreign_documents/doomemacs")
|
||||||
}));
|
}));
|
||||||
|
let layer = layer.chain(compare_group("literate_build_emacs", || {
|
||||||
|
compare_all_org_document("/foreign_documents/literate_build_emacs")
|
||||||
|
}));
|
||||||
|
|
||||||
let running_tests: Vec<_> = layer.map(|c| tokio::spawn(c.run_test())).collect();
|
let running_tests: Vec<_> = layer.map(|c| tokio::spawn(c.run_test())).collect();
|
||||||
let mut any_failed = false;
|
let mut any_failed = false;
|
||||||
|
|||||||
10
src/bin_wasm.rs
Normal file
10
src/bin_wasm.rs
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
use wasm_bindgen::prelude::wasm_bindgen;
|
||||||
|
|
||||||
|
#[wasm_bindgen]
|
||||||
|
pub fn parse_org(org_contents: &str) -> wasm_bindgen::JsValue {
|
||||||
|
organic::wasm_cli::parse_org(org_contents)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
62
src/bin_wasm_test.rs
Normal file
62
src/bin_wasm_test.rs
Normal file
@@ -0,0 +1,62 @@
|
|||||||
|
#![feature(exact_size_is_empty)]
|
||||||
|
#![feature(exit_status_error)]
|
||||||
|
use std::io::Read;
|
||||||
|
|
||||||
|
use organic::wasm_test::wasm_run_anonymous_compare;
|
||||||
|
use organic::wasm_test::wasm_run_compare_on_file;
|
||||||
|
|
||||||
|
#[cfg(feature = "tracing")]
|
||||||
|
use crate::init_tracing::init_telemetry;
|
||||||
|
#[cfg(feature = "tracing")]
|
||||||
|
use crate::init_tracing::shutdown_telemetry;
|
||||||
|
#[cfg(feature = "tracing")]
|
||||||
|
mod init_tracing;
|
||||||
|
|
||||||
|
#[cfg(not(feature = "tracing"))]
|
||||||
|
fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||||
|
let rt = tokio::runtime::Runtime::new()?;
|
||||||
|
rt.block_on(async {
|
||||||
|
let main_body_result = main_body().await;
|
||||||
|
main_body_result
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "tracing")]
|
||||||
|
fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||||
|
let rt = tokio::runtime::Runtime::new()?;
|
||||||
|
rt.block_on(async {
|
||||||
|
init_telemetry()?;
|
||||||
|
let main_body_result = main_body().await;
|
||||||
|
shutdown_telemetry()?;
|
||||||
|
main_body_result
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
|
||||||
|
async fn main_body() -> Result<(), Box<dyn std::error::Error>> {
|
||||||
|
let args = std::env::args().skip(1);
|
||||||
|
if args.is_empty() {
|
||||||
|
let org_contents = read_stdin_to_string()?;
|
||||||
|
if wasm_run_anonymous_compare(org_contents).await? {
|
||||||
|
} else {
|
||||||
|
Err("Diff results do not match.")?;
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
} else {
|
||||||
|
for arg in args {
|
||||||
|
if wasm_run_compare_on_file(arg).await? {
|
||||||
|
} else {
|
||||||
|
Err("Diff results do not match.")?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn read_stdin_to_string() -> Result<String, Box<dyn std::error::Error>> {
|
||||||
|
let mut stdin_contents = String::new();
|
||||||
|
std::io::stdin()
|
||||||
|
.lock()
|
||||||
|
.read_to_string(&mut stdin_contents)?;
|
||||||
|
Ok(stdin_contents)
|
||||||
|
}
|
||||||
@@ -1,16 +1,16 @@
|
|||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
|
|
||||||
use crate::compare::diff::compare_document;
|
use crate::compare::diff::compare_document;
|
||||||
use crate::compare::diff::DiffResult;
|
|
||||||
use crate::compare::parse::emacs_parse_anonymous_org_document;
|
|
||||||
use crate::compare::parse::emacs_parse_file_org_document;
|
|
||||||
use crate::compare::parse::get_emacs_version;
|
|
||||||
use crate::compare::parse::get_org_mode_version;
|
|
||||||
use crate::compare::sexp::sexp;
|
|
||||||
use crate::context::GlobalSettings;
|
use crate::context::GlobalSettings;
|
||||||
use crate::context::LocalFileAccessInterface;
|
use crate::context::LocalFileAccessInterface;
|
||||||
use crate::parser::parse_file_with_settings;
|
use crate::parser::parse_file_with_settings;
|
||||||
use crate::parser::parse_with_settings;
|
use crate::parser::parse_with_settings;
|
||||||
|
use crate::util::cli::emacs_parse_anonymous_org_document;
|
||||||
|
use crate::util::cli::emacs_parse_file_org_document;
|
||||||
|
use crate::util::cli::print_versions;
|
||||||
|
use crate::util::elisp::sexp;
|
||||||
|
use crate::util::terminal::foreground_color;
|
||||||
|
use crate::util::terminal::reset_color;
|
||||||
|
|
||||||
pub async fn run_anonymous_compare<P: AsRef<str>>(
|
pub async fn run_anonymous_compare<P: AsRef<str>>(
|
||||||
org_contents: P,
|
org_contents: P,
|
||||||
@@ -68,8 +68,8 @@ pub async fn run_anonymous_compare_with_settings<'g, 's, P: AsRef<str>>(
|
|||||||
} else if !silent {
|
} else if !silent {
|
||||||
println!(
|
println!(
|
||||||
"{color}Entire document passes.{reset}",
|
"{color}Entire document passes.{reset}",
|
||||||
color = DiffResult::foreground_color(0, 255, 0),
|
color = foreground_color(0, 255, 0),
|
||||||
reset = DiffResult::reset_color(),
|
reset = reset_color(),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -121,19 +121,10 @@ pub async fn run_compare_on_file_with_settings<'g, 's, P: AsRef<Path>>(
|
|||||||
} else if !silent {
|
} else if !silent {
|
||||||
println!(
|
println!(
|
||||||
"{color}Entire document passes.{reset}",
|
"{color}Entire document passes.{reset}",
|
||||||
color = DiffResult::foreground_color(0, 255, 0),
|
color = foreground_color(0, 255, 0),
|
||||||
reset = DiffResult::reset_color(),
|
reset = reset_color(),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(true)
|
Ok(true)
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn print_versions() -> Result<(), Box<dyn std::error::Error>> {
|
|
||||||
eprintln!("Using emacs version: {}", get_emacs_version().await?.trim());
|
|
||||||
eprintln!(
|
|
||||||
"Using org-mode version: {}",
|
|
||||||
get_org_mode_version().await?.trim()
|
|
||||||
);
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
use std::borrow::Borrow;
|
||||||
|
use std::borrow::Cow;
|
||||||
use std::collections::BTreeSet;
|
use std::collections::BTreeSet;
|
||||||
use std::fmt::Debug;
|
use std::fmt::Debug;
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
@@ -7,8 +9,6 @@ use super::diff::artificial_owned_diff_scope;
|
|||||||
use super::diff::compare_ast_node;
|
use super::diff::compare_ast_node;
|
||||||
use super::diff::DiffEntry;
|
use super::diff::DiffEntry;
|
||||||
use super::diff::DiffStatus;
|
use super::diff::DiffStatus;
|
||||||
use super::sexp::unquote;
|
|
||||||
use super::sexp::Token;
|
|
||||||
use super::util::get_property;
|
use super::util::get_property;
|
||||||
use super::util::get_property_numeric;
|
use super::util::get_property_numeric;
|
||||||
use super::util::get_property_quoted_string;
|
use super::util::get_property_quoted_string;
|
||||||
@@ -18,6 +18,8 @@ use crate::types::CharOffsetInLine;
|
|||||||
use crate::types::LineNumber;
|
use crate::types::LineNumber;
|
||||||
use crate::types::RetainLabels;
|
use crate::types::RetainLabels;
|
||||||
use crate::types::SwitchNumberLines;
|
use crate::types::SwitchNumberLines;
|
||||||
|
use crate::util::elisp::unquote;
|
||||||
|
use crate::util::elisp::Token;
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub(crate) enum EmacsField<'s> {
|
pub(crate) enum EmacsField<'s> {
|
||||||
@@ -262,11 +264,11 @@ pub(crate) fn compare_property_set_of_quoted_string<
|
|||||||
.iter()
|
.iter()
|
||||||
.map(|e| e.as_atom())
|
.map(|e| e.as_atom())
|
||||||
.collect::<Result<Vec<_>, _>>()?;
|
.collect::<Result<Vec<_>, _>>()?;
|
||||||
let value: Vec<String> = value
|
let value: Vec<Cow<'_, str>> = value
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(unquote)
|
.map(unquote)
|
||||||
.collect::<Result<Vec<_>, _>>()?;
|
.collect::<Result<Vec<_>, _>>()?;
|
||||||
let value: BTreeSet<&str> = value.iter().map(|e| e.as_str()).collect();
|
let value: BTreeSet<&str> = value.iter().map(|e| e.borrow()).collect();
|
||||||
let mismatched: Vec<_> = value.symmetric_difference(&rust_value).copied().collect();
|
let mismatched: Vec<_> = value.symmetric_difference(&rust_value).copied().collect();
|
||||||
if !mismatched.is_empty() {
|
if !mismatched.is_empty() {
|
||||||
let this_status = DiffStatus::Bad;
|
let this_status = DiffStatus::Bad;
|
||||||
@@ -546,6 +548,21 @@ where
|
|||||||
let mut full_status: Vec<DiffEntry<'b, 's>> = Vec::with_capacity(outer_rust_list.len());
|
let mut full_status: Vec<DiffEntry<'b, 's>> = Vec::with_capacity(outer_rust_list.len());
|
||||||
|
|
||||||
for (kw_e, kw_r) in outer_emacs_list.iter().zip(outer_rust_list) {
|
for (kw_e, kw_r) in outer_emacs_list.iter().zip(outer_rust_list) {
|
||||||
|
match (kw_e.as_atom(), kw_r) {
|
||||||
|
(Ok("nil"), (None, mandatory_value)) if mandatory_value.is_empty() => {
|
||||||
|
// If its an empty keyword then it becomes nil in the elisp.
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
(Ok("nil"), _) => {
|
||||||
|
let this_status = DiffStatus::Bad;
|
||||||
|
let message = Some(format!(
|
||||||
|
"{} mismatch (emacs != rust) {:?} != {:?}",
|
||||||
|
emacs_field, kw_e, kw_r
|
||||||
|
));
|
||||||
|
return Ok(ComparePropertiesResult::SelfChange(this_status, message));
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
let kw_e = kw_e.as_list()?;
|
let kw_e = kw_e.as_list()?;
|
||||||
let child_status_length = kw_r.1.len() + kw_r.0.as_ref().map(|opt| opt.len()).unwrap_or(0);
|
let child_status_length = kw_r.1.len() + kw_r.0.as_ref().map(|opt| opt.len()).unwrap_or(0);
|
||||||
let mut child_status: Vec<DiffEntry<'b, 's>> = Vec::with_capacity(child_status_length);
|
let mut child_status: Vec<DiffEntry<'b, 's>> = Vec::with_capacity(child_status_length);
|
||||||
@@ -554,6 +571,17 @@ where
|
|||||||
let mut kw_e = kw_e.iter();
|
let mut kw_e = kw_e.iter();
|
||||||
// First element is a list representing the mandatory value.
|
// First element is a list representing the mandatory value.
|
||||||
if let Some(val_e) = kw_e.next() {
|
if let Some(val_e) = kw_e.next() {
|
||||||
|
match (val_e.as_atom(), kw_r) {
|
||||||
|
(Ok("nil"), (_, mandatory_value)) if mandatory_value.is_empty() => {}
|
||||||
|
(Ok("nil"), _) => {
|
||||||
|
let this_status = DiffStatus::Bad;
|
||||||
|
let message = Some(format!(
|
||||||
|
"{} mismatch (emacs != rust) {:?} != {:?}",
|
||||||
|
emacs_field, kw_e, kw_r
|
||||||
|
));
|
||||||
|
return Ok(ComparePropertiesResult::SelfChange(this_status, message));
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
let el = val_e.as_list()?;
|
let el = val_e.as_list()?;
|
||||||
if el.len() != kw_r.1.len() {
|
if el.len() != kw_r.1.len() {
|
||||||
let this_status = DiffStatus::Bad;
|
let this_status = DiffStatus::Bad;
|
||||||
@@ -566,6 +594,8 @@ where
|
|||||||
for (e, r) in el.iter().zip(kw_r.1.iter()) {
|
for (e, r) in el.iter().zip(kw_r.1.iter()) {
|
||||||
child_status.push(compare_ast_node(source, e, r.into())?);
|
child_status.push(compare_ast_node(source, e, r.into())?);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
} else {
|
} else {
|
||||||
let this_status = DiffStatus::Bad;
|
let this_status = DiffStatus::Bad;
|
||||||
let message = Some(format!(
|
let message = Some(format!(
|
||||||
@@ -653,7 +683,7 @@ pub(crate) fn compare_property_number_lines<
|
|||||||
(Some(number_lines), Some(rust_number_lines)) => {
|
(Some(number_lines), Some(rust_number_lines)) => {
|
||||||
let token_list = number_lines.as_list()?;
|
let token_list = number_lines.as_list()?;
|
||||||
let number_type = token_list
|
let number_type = token_list
|
||||||
.get(0)
|
.first()
|
||||||
.map(Token::as_atom)
|
.map(Token::as_atom)
|
||||||
.map_or(Ok(None), |r| r.map(Some))?
|
.map_or(Ok(None), |r| r.map(Some))?
|
||||||
.ok_or(":number-lines should have a type.")?;
|
.ok_or(":number-lines should have a type.")?;
|
||||||
|
|||||||
@@ -16,10 +16,6 @@ use super::compare_field::compare_property_retain_labels;
|
|||||||
use super::compare_field::compare_property_set_of_quoted_string;
|
use super::compare_field::compare_property_set_of_quoted_string;
|
||||||
use super::compare_field::compare_property_single_ast_node;
|
use super::compare_field::compare_property_single_ast_node;
|
||||||
use super::compare_field::compare_property_unquoted_atom;
|
use super::compare_field::compare_property_unquoted_atom;
|
||||||
use super::elisp_fact::ElispFact;
|
|
||||||
use super::elisp_fact::GetElispFact;
|
|
||||||
use super::sexp::unquote;
|
|
||||||
use super::sexp::Token;
|
|
||||||
use super::util::affiliated_keywords_names;
|
use super::util::affiliated_keywords_names;
|
||||||
use super::util::assert_no_children;
|
use super::util::assert_no_children;
|
||||||
use super::util::compare_additional_properties;
|
use super::util::compare_additional_properties;
|
||||||
@@ -57,7 +53,6 @@ use crate::types::FixedWidthArea;
|
|||||||
use crate::types::FootnoteDefinition;
|
use crate::types::FootnoteDefinition;
|
||||||
use crate::types::FootnoteReference;
|
use crate::types::FootnoteReference;
|
||||||
use crate::types::FootnoteReferenceType;
|
use crate::types::FootnoteReferenceType;
|
||||||
use crate::types::GetStandardProperties;
|
|
||||||
use crate::types::Heading;
|
use crate::types::Heading;
|
||||||
use crate::types::HorizontalRule;
|
use crate::types::HorizontalRule;
|
||||||
use crate::types::Hour;
|
use crate::types::Hour;
|
||||||
@@ -110,6 +105,12 @@ use crate::types::Verbatim;
|
|||||||
use crate::types::VerseBlock;
|
use crate::types::VerseBlock;
|
||||||
use crate::types::WarningDelayType;
|
use crate::types::WarningDelayType;
|
||||||
use crate::types::Year;
|
use crate::types::Year;
|
||||||
|
use crate::util::elisp::unquote;
|
||||||
|
use crate::util::elisp::Token;
|
||||||
|
use crate::util::elisp_fact::ElispFact;
|
||||||
|
use crate::util::elisp_fact::GetElispFact;
|
||||||
|
use crate::util::terminal::foreground_color;
|
||||||
|
use crate::util::terminal::reset_color;
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub enum DiffEntry<'b, 's> {
|
pub enum DiffEntry<'b, 's> {
|
||||||
@@ -128,7 +129,7 @@ pub struct DiffResult<'b, 's> {
|
|||||||
emacs_token: &'b Token<'s>,
|
emacs_token: &'b Token<'s>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, PartialEq)]
|
#[derive(Debug)]
|
||||||
pub(crate) enum DiffStatus {
|
pub(crate) enum DiffStatus {
|
||||||
Good,
|
Good,
|
||||||
Bad,
|
Bad,
|
||||||
@@ -164,7 +165,7 @@ impl<'b, 's> DiffEntry<'b, 's> {
|
|||||||
|
|
||||||
fn is_immediately_bad(&self) -> bool {
|
fn is_immediately_bad(&self) -> bool {
|
||||||
match self {
|
match self {
|
||||||
DiffEntry::DiffResult(diff) => diff.status == DiffStatus::Bad,
|
DiffEntry::DiffResult(diff) => matches!(diff.status, DiffStatus::Bad),
|
||||||
DiffEntry::DiffLayer(_) => false,
|
DiffEntry::DiffLayer(_) => false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -201,21 +202,21 @@ impl<'b, 's> DiffResult<'b, 's> {
|
|||||||
if self.has_bad_children() {
|
if self.has_bad_children() {
|
||||||
format!(
|
format!(
|
||||||
"{color}BADCHILD{reset}",
|
"{color}BADCHILD{reset}",
|
||||||
color = DiffResult::foreground_color(255, 255, 0),
|
color = foreground_color(255, 255, 0),
|
||||||
reset = DiffResult::reset_color(),
|
reset = reset_color(),
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
format!(
|
format!(
|
||||||
"{color}GOOD{reset}",
|
"{color}GOOD{reset}",
|
||||||
color = DiffResult::foreground_color(0, 255, 0),
|
color = foreground_color(0, 255, 0),
|
||||||
reset = DiffResult::reset_color(),
|
reset = reset_color(),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
DiffStatus::Bad => format!(
|
DiffStatus::Bad => format!(
|
||||||
"{color}BAD{reset}",
|
"{color}BAD{reset}",
|
||||||
color = DiffResult::foreground_color(255, 0, 0),
|
color = foreground_color(255, 0, 0),
|
||||||
reset = DiffResult::reset_color(),
|
reset = reset_color(),
|
||||||
),
|
),
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -240,45 +241,6 @@ impl<'b, 's> DiffResult<'b, 's> {
|
|||||||
.iter()
|
.iter()
|
||||||
.any(|child| child.is_immediately_bad() || child.has_bad_children())
|
.any(|child| child.is_immediately_bad() || child.has_bad_children())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn foreground_color(red: u8, green: u8, blue: u8) -> String {
|
|
||||||
if DiffResult::should_use_color() {
|
|
||||||
format!(
|
|
||||||
"\x1b[38;2;{red};{green};{blue}m",
|
|
||||||
red = red,
|
|
||||||
green = green,
|
|
||||||
blue = blue
|
|
||||||
)
|
|
||||||
} else {
|
|
||||||
String::new()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[allow(dead_code)]
|
|
||||||
pub(crate) fn background_color(red: u8, green: u8, blue: u8) -> String {
|
|
||||||
if DiffResult::should_use_color() {
|
|
||||||
format!(
|
|
||||||
"\x1b[48;2;{red};{green};{blue}m",
|
|
||||||
red = red,
|
|
||||||
green = green,
|
|
||||||
blue = blue
|
|
||||||
)
|
|
||||||
} else {
|
|
||||||
String::new()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn reset_color() -> &'static str {
|
|
||||||
if DiffResult::should_use_color() {
|
|
||||||
"\x1b[0m"
|
|
||||||
} else {
|
|
||||||
""
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn should_use_color() -> bool {
|
|
||||||
!std::env::var("NO_COLOR").is_ok_and(|val| !val.is_empty())
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'b, 's> DiffLayer<'b, 's> {
|
impl<'b, 's> DiffLayer<'b, 's> {
|
||||||
@@ -296,14 +258,14 @@ impl<'b, 's> DiffLayer<'b, 's> {
|
|||||||
let status_text = if self.has_bad_children() {
|
let status_text = if self.has_bad_children() {
|
||||||
format!(
|
format!(
|
||||||
"{color}BADCHILD{reset}",
|
"{color}BADCHILD{reset}",
|
||||||
color = DiffResult::foreground_color(255, 255, 0),
|
color = foreground_color(255, 255, 0),
|
||||||
reset = DiffResult::reset_color(),
|
reset = reset_color(),
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
format!(
|
format!(
|
||||||
"{color}GOOD{reset}",
|
"{color}GOOD{reset}",
|
||||||
color = DiffResult::foreground_color(0, 255, 0),
|
color = foreground_color(0, 255, 0),
|
||||||
reset = DiffResult::reset_color(),
|
reset = reset_color(),
|
||||||
)
|
)
|
||||||
};
|
};
|
||||||
println!(
|
println!(
|
||||||
@@ -413,7 +375,7 @@ pub(crate) fn compare_ast_node<'b, 's>(
|
|||||||
name: rust.get_elisp_fact().get_elisp_name(),
|
name: rust.get_elisp_fact().get_elisp_name(),
|
||||||
message: Some(e.to_string()),
|
message: Some(e.to_string()),
|
||||||
children: Vec::new(),
|
children: Vec::new(),
|
||||||
rust_source: rust.get_standard_properties().get_source(),
|
rust_source: rust.get_source(),
|
||||||
emacs_token: emacs,
|
emacs_token: emacs,
|
||||||
}
|
}
|
||||||
.into()
|
.into()
|
||||||
@@ -1576,7 +1538,7 @@ fn compare_example_block<'b, 's>(
|
|||||||
[],
|
[],
|
||||||
(
|
(
|
||||||
EmacsField::Required(":value"),
|
EmacsField::Required(":value"),
|
||||||
|r| Some(r.contents.as_str()),
|
|r| Some(r.get_value()),
|
||||||
compare_property_quoted_string
|
compare_property_quoted_string
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
@@ -1654,7 +1616,7 @@ fn compare_export_block<'b, 's>(
|
|||||||
),
|
),
|
||||||
(
|
(
|
||||||
EmacsField::Required(":value"),
|
EmacsField::Required(":value"),
|
||||||
|r| Some(r.contents.as_str()),
|
|r| Some(r.get_value()),
|
||||||
compare_property_quoted_string
|
compare_property_quoted_string
|
||||||
)
|
)
|
||||||
) {
|
) {
|
||||||
@@ -1702,7 +1664,7 @@ fn compare_src_block<'b, 's>(
|
|||||||
),
|
),
|
||||||
(
|
(
|
||||||
EmacsField::Required(":value"),
|
EmacsField::Required(":value"),
|
||||||
|r| Some(r.contents.as_str()),
|
|r| Some(r.get_value()),
|
||||||
compare_property_quoted_string
|
compare_property_quoted_string
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
@@ -2153,7 +2115,7 @@ fn compare_plain_text<'b, 's>(
|
|||||||
let text = emacs.as_text()?;
|
let text = emacs.as_text()?;
|
||||||
let start_ind: usize = text
|
let start_ind: usize = text
|
||||||
.properties
|
.properties
|
||||||
.get(0)
|
.first()
|
||||||
.expect("Should have start index.")
|
.expect("Should have start index.")
|
||||||
.as_atom()?
|
.as_atom()?
|
||||||
.parse()?;
|
.parse()?;
|
||||||
|
|||||||
@@ -2,10 +2,7 @@
|
|||||||
mod compare;
|
mod compare;
|
||||||
mod compare_field;
|
mod compare_field;
|
||||||
mod diff;
|
mod diff;
|
||||||
mod elisp_fact;
|
|
||||||
mod macros;
|
mod macros;
|
||||||
mod parse;
|
|
||||||
mod sexp;
|
|
||||||
mod util;
|
mod util;
|
||||||
pub use compare::run_anonymous_compare;
|
pub use compare::run_anonymous_compare;
|
||||||
pub use compare::run_anonymous_compare_with_settings;
|
pub use compare::run_anonymous_compare_with_settings;
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
use std::borrow::Cow;
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
|
|
||||||
use super::compare_field::compare_property_list_of_quoted_string;
|
use super::compare_field::compare_property_list_of_quoted_string;
|
||||||
@@ -7,15 +8,15 @@ use super::compare_field::compare_property_quoted_string;
|
|||||||
use super::compare_field::ComparePropertiesResult;
|
use super::compare_field::ComparePropertiesResult;
|
||||||
use super::diff::DiffEntry;
|
use super::diff::DiffEntry;
|
||||||
use super::diff::DiffStatus;
|
use super::diff::DiffStatus;
|
||||||
use super::elisp_fact::GetElispFact;
|
|
||||||
use super::sexp::Token;
|
|
||||||
use crate::compare::diff::compare_ast_node;
|
use crate::compare::diff::compare_ast_node;
|
||||||
use crate::compare::sexp::unquote;
|
|
||||||
use crate::types::AffiliatedKeywordValue;
|
use crate::types::AffiliatedKeywordValue;
|
||||||
use crate::types::AstNode;
|
use crate::types::AstNode;
|
||||||
use crate::types::GetAffiliatedKeywords;
|
use crate::types::GetAffiliatedKeywords;
|
||||||
use crate::types::GetStandardProperties;
|
|
||||||
use crate::types::StandardProperties;
|
use crate::types::StandardProperties;
|
||||||
|
use crate::util::elisp::get_emacs_standard_properties;
|
||||||
|
use crate::util::elisp::unquote;
|
||||||
|
use crate::util::elisp::Token;
|
||||||
|
use crate::util::elisp_fact::GetElispFact;
|
||||||
|
|
||||||
/// Check if the child string slice is a slice of the parent string slice.
|
/// Check if the child string slice is a slice of the parent string slice.
|
||||||
fn is_slice_of(parent: &str, child: &str) -> bool {
|
fn is_slice_of(parent: &str, child: &str) -> bool {
|
||||||
@@ -29,32 +30,29 @@ fn is_slice_of(parent: &str, child: &str) -> bool {
|
|||||||
/// Get the byte offset into source that the rust object exists at.
|
/// Get the byte offset into source that the rust object exists at.
|
||||||
///
|
///
|
||||||
/// These offsets are zero-based unlike the elisp ones.
|
/// These offsets are zero-based unlike the elisp ones.
|
||||||
fn get_rust_byte_offsets<'b, 's, S: StandardProperties<'s> + ?Sized>(
|
fn get_rust_byte_offsets(original_document: &str, subset: &str) -> (usize, usize) {
|
||||||
original_document: &'s str,
|
debug_assert!(is_slice_of(original_document, subset));
|
||||||
rust_ast_node: &'b S,
|
let offset = subset.as_ptr() as usize - original_document.as_ptr() as usize;
|
||||||
) -> (usize, usize) {
|
let end = offset + subset.len();
|
||||||
let rust_object_source = rust_ast_node.get_source();
|
|
||||||
debug_assert!(is_slice_of(original_document, rust_object_source));
|
|
||||||
let offset = rust_object_source.as_ptr() as usize - original_document.as_ptr() as usize;
|
|
||||||
let end = offset + rust_object_source.len();
|
|
||||||
(offset, end)
|
(offset, end)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn compare_standard_properties<
|
pub(crate) fn compare_standard_properties<
|
||||||
'b,
|
'b,
|
||||||
's,
|
's,
|
||||||
S: GetStandardProperties<'s> + GetElispFact<'s> + ?Sized,
|
S: StandardProperties<'s> + GetElispFact<'s> + ?Sized,
|
||||||
>(
|
>(
|
||||||
original_document: &'s str,
|
original_document: &'s str,
|
||||||
emacs: &'b Token<'s>,
|
emacs: &'b Token<'s>,
|
||||||
rust: &'b S,
|
rust: &'b S,
|
||||||
) -> Result<(), Box<dyn std::error::Error>> {
|
) -> Result<(), Box<dyn std::error::Error>> {
|
||||||
assert_name(emacs, rust.get_elisp_fact().get_elisp_name())?;
|
assert_name(emacs, rust.get_elisp_fact().get_elisp_name())?;
|
||||||
assert_bounds(original_document, emacs, rust.get_standard_properties())?;
|
assert_bounds(original_document, emacs, rust)?;
|
||||||
|
assert_post_blank(emacs, rust)?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn assert_name<S: AsRef<str>>(
|
fn assert_name<S: AsRef<str>>(
|
||||||
emacs: &Token<'_>,
|
emacs: &Token<'_>,
|
||||||
name: S,
|
name: S,
|
||||||
) -> Result<(), Box<dyn std::error::Error>> {
|
) -> Result<(), Box<dyn std::error::Error>> {
|
||||||
@@ -77,101 +75,75 @@ pub(crate) fn assert_name<S: AsRef<str>>(
|
|||||||
/// Assert that the character ranges defined by upstream org-mode's :standard-properties match the slices in Organic's StandardProperties.
|
/// Assert that the character ranges defined by upstream org-mode's :standard-properties match the slices in Organic's StandardProperties.
|
||||||
///
|
///
|
||||||
/// This does **not** handle plain text because plain text is a special case.
|
/// This does **not** handle plain text because plain text is a special case.
|
||||||
pub(crate) fn assert_bounds<'b, 's, S: StandardProperties<'s> + ?Sized>(
|
fn assert_bounds<'b, 's, S: StandardProperties<'s> + ?Sized>(
|
||||||
original_document: &'s str,
|
original_document: &'s str,
|
||||||
emacs: &'b Token<'s>,
|
emacs: &'b Token<'s>,
|
||||||
rust: &'b S,
|
rust: &'b S,
|
||||||
) -> Result<(), Box<dyn std::error::Error>> {
|
) -> Result<(), Box<dyn std::error::Error>> {
|
||||||
let standard_properties = get_emacs_standard_properties(emacs)?; // 1-based
|
let standard_properties = get_emacs_standard_properties(emacs)?; // 1-based
|
||||||
|
|
||||||
|
// Check begin/end
|
||||||
|
{
|
||||||
let (begin, end) = (
|
let (begin, end) = (
|
||||||
standard_properties
|
standard_properties
|
||||||
.begin
|
.begin
|
||||||
.ok_or("Token should have a begin.")?,
|
.ok_or("Token should have a begin.")?,
|
||||||
standard_properties.end.ok_or("Token should have an end.")?,
|
standard_properties.end.ok_or("Token should have an end.")?,
|
||||||
);
|
);
|
||||||
let (rust_begin, rust_end) = get_rust_byte_offsets(original_document, rust); // 0-based
|
let (rust_begin, rust_end) = get_rust_byte_offsets(original_document, rust.get_source()); // 0-based
|
||||||
let rust_begin_char_offset = original_document[..rust_begin].chars().count() + 1; // 1-based
|
let rust_begin_char_offset = original_document[..rust_begin].chars().count() + 1; // 1-based
|
||||||
let rust_end_char_offset =
|
let rust_end_char_offset =
|
||||||
rust_begin_char_offset + original_document[rust_begin..rust_end].chars().count(); // 1-based
|
rust_begin_char_offset + original_document[rust_begin..rust_end].chars().count(); // 1-based
|
||||||
if rust_begin_char_offset != begin || rust_end_char_offset != end {
|
if rust_begin_char_offset != begin || rust_end_char_offset != 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, rust_end = rust_end_char_offset, emacs_begin=begin, emacs_end=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, rust_end = rust_end_char_offset, emacs_begin=begin, emacs_end=end))?;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check contents-begin/contents-end
|
||||||
|
{
|
||||||
|
if let Some(rust_contents) = rust.get_contents() {
|
||||||
|
let (begin, end) = (
|
||||||
|
standard_properties
|
||||||
|
.contents_begin
|
||||||
|
.ok_or("Token should have a contents-begin.")?,
|
||||||
|
standard_properties
|
||||||
|
.contents_end
|
||||||
|
.ok_or("Token should have an contents-end.")?,
|
||||||
|
);
|
||||||
|
let (rust_begin, rust_end) = get_rust_byte_offsets(original_document, rust_contents); // 0-based
|
||||||
|
let rust_begin_char_offset = original_document[..rust_begin].chars().count() + 1; // 1-based
|
||||||
|
let rust_end_char_offset =
|
||||||
|
rust_begin_char_offset + original_document[rust_begin..rust_end].chars().count(); // 1-based
|
||||||
|
if rust_begin_char_offset != begin || rust_end_char_offset != end {
|
||||||
|
Err(format!("Rust contents bounds (in chars) ({rust_begin}, {rust_end}) do not match emacs contents bounds ({emacs_begin}, {emacs_end})", rust_begin = rust_begin_char_offset, rust_end = rust_end_char_offset, emacs_begin=begin, emacs_end=end))?;
|
||||||
|
}
|
||||||
|
} else if standard_properties.contents_begin.is_some()
|
||||||
|
|| standard_properties.contents_end.is_some()
|
||||||
|
{
|
||||||
|
Err(format!("Rust contents is None but emacs contents bounds are ({emacs_begin:?}, {emacs_end:?})", emacs_begin=standard_properties.contents_begin, emacs_end=standard_properties.contents_end))?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
struct EmacsStandardProperties {
|
/// Assert that the post blank matches between emacs and organic.
|
||||||
begin: Option<usize>,
|
///
|
||||||
#[allow(dead_code)]
|
/// This does **not** handle plain text because plain text is a special case.
|
||||||
post_affiliated: Option<usize>,
|
fn assert_post_blank<'b, 's, S: StandardProperties<'s> + ?Sized>(
|
||||||
#[allow(dead_code)]
|
emacs: &'b Token<'s>,
|
||||||
contents_begin: Option<usize>,
|
rust: &'b S,
|
||||||
#[allow(dead_code)]
|
) -> Result<(), Box<dyn std::error::Error>> {
|
||||||
contents_end: Option<usize>,
|
let standard_properties = get_emacs_standard_properties(emacs)?; // 1-based
|
||||||
end: Option<usize>,
|
let rust_post_blank = rust.get_post_blank();
|
||||||
#[allow(dead_code)]
|
let emacs_post_blank = standard_properties
|
||||||
post_blank: Option<usize>,
|
.post_blank
|
||||||
|
.ok_or("Token should have a post-blank.")?;
|
||||||
|
if rust_post_blank as usize != emacs_post_blank {
|
||||||
|
Err(format!("Rust post-blank {rust_post_blank} does not match emacs post-blank ({emacs_post_blank})", rust_post_blank = rust_post_blank, emacs_post_blank = emacs_post_blank))?;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_emacs_standard_properties(
|
Ok(())
|
||||||
emacs: &Token<'_>,
|
|
||||||
) -> Result<EmacsStandardProperties, Box<dyn std::error::Error>> {
|
|
||||||
let children = emacs.as_list()?;
|
|
||||||
let attributes_child = children.get(1).ok_or("Should have an attributes child.")?;
|
|
||||||
let attributes_map = attributes_child.as_map()?;
|
|
||||||
let standard_properties = attributes_map.get(":standard-properties");
|
|
||||||
Ok(if standard_properties.is_some() {
|
|
||||||
let mut std_props = standard_properties
|
|
||||||
.expect("if statement proves its Some")
|
|
||||||
.as_vector()?
|
|
||||||
.iter();
|
|
||||||
let begin = maybe_token_to_usize(std_props.next())?;
|
|
||||||
let post_affiliated = maybe_token_to_usize(std_props.next())?;
|
|
||||||
let contents_begin = maybe_token_to_usize(std_props.next())?;
|
|
||||||
let contents_end = maybe_token_to_usize(std_props.next())?;
|
|
||||||
let end = maybe_token_to_usize(std_props.next())?;
|
|
||||||
let post_blank = maybe_token_to_usize(std_props.next())?;
|
|
||||||
EmacsStandardProperties {
|
|
||||||
begin,
|
|
||||||
post_affiliated,
|
|
||||||
contents_begin,
|
|
||||||
contents_end,
|
|
||||||
end,
|
|
||||||
post_blank,
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
let begin = maybe_token_to_usize(attributes_map.get(":begin").copied())?;
|
|
||||||
let end = maybe_token_to_usize(attributes_map.get(":end").copied())?;
|
|
||||||
let contents_begin = maybe_token_to_usize(attributes_map.get(":contents-begin").copied())?;
|
|
||||||
let contents_end = maybe_token_to_usize(attributes_map.get(":contents-end").copied())?;
|
|
||||||
let post_blank = maybe_token_to_usize(attributes_map.get(":post-blank").copied())?;
|
|
||||||
let post_affiliated =
|
|
||||||
maybe_token_to_usize(attributes_map.get(":post-affiliated").copied())?;
|
|
||||||
EmacsStandardProperties {
|
|
||||||
begin,
|
|
||||||
post_affiliated,
|
|
||||||
contents_begin,
|
|
||||||
contents_end,
|
|
||||||
end,
|
|
||||||
post_blank,
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
fn maybe_token_to_usize(
|
|
||||||
token: Option<&Token<'_>>,
|
|
||||||
) -> Result<Option<usize>, Box<dyn std::error::Error>> {
|
|
||||||
Ok(token
|
|
||||||
.map(|token| token.as_atom())
|
|
||||||
.map_or(Ok(None), |r| r.map(Some))?
|
|
||||||
.and_then(|val| {
|
|
||||||
if val == "nil" {
|
|
||||||
None
|
|
||||||
} else {
|
|
||||||
Some(val.parse::<usize>())
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.map_or(Ok(None), |r| r.map(Some))?)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get a named property from the emacs token.
|
/// Get a named property from the emacs token.
|
||||||
@@ -206,10 +178,10 @@ pub(crate) fn get_property_unquoted_atom<'s>(
|
|||||||
/// Get a named property containing an quoted string from the emacs token.
|
/// Get a named property containing an quoted string from the emacs token.
|
||||||
///
|
///
|
||||||
/// Returns None if key is not found.
|
/// Returns None if key is not found.
|
||||||
pub(crate) fn get_property_quoted_string(
|
pub(crate) fn get_property_quoted_string<'s>(
|
||||||
emacs: &Token<'_>,
|
emacs: &Token<'s>,
|
||||||
key: &str,
|
key: &str,
|
||||||
) -> Result<Option<String>, Box<dyn std::error::Error>> {
|
) -> Result<Option<Cow<'s, str>>, Box<dyn std::error::Error>> {
|
||||||
get_property(emacs, key)?
|
get_property(emacs, key)?
|
||||||
.map(Token::as_atom)
|
.map(Token::as_atom)
|
||||||
.map_or(Ok(None), |r| r.map(Some))?
|
.map_or(Ok(None), |r| r.map(Some))?
|
||||||
@@ -240,7 +212,7 @@ where
|
|||||||
pub(crate) fn compare_children<'b, 's, 'x, RC>(
|
pub(crate) fn compare_children<'b, 's, 'x, RC>(
|
||||||
source: &'s str,
|
source: &'s str,
|
||||||
emacs: &'b Token<'s>,
|
emacs: &'b Token<'s>,
|
||||||
rust_children: &'x Vec<RC>,
|
rust_children: &'x [RC],
|
||||||
child_status: &mut Vec<DiffEntry<'b, 's>>,
|
child_status: &mut Vec<DiffEntry<'b, 's>>,
|
||||||
this_status: &mut DiffStatus,
|
this_status: &mut DiffStatus,
|
||||||
message: &mut Option<String>,
|
message: &mut Option<String>,
|
||||||
|
|||||||
@@ -6,8 +6,6 @@ pub(crate) type Res<T, U> = IResult<T, U, CustomError>;
|
|||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub enum CustomError {
|
pub enum CustomError {
|
||||||
#[allow(dead_code)]
|
|
||||||
Text(String),
|
|
||||||
Static(&'static str),
|
Static(&'static str),
|
||||||
IO(std::io::Error),
|
IO(std::io::Error),
|
||||||
Parser(ErrorKind),
|
Parser(ErrorKind),
|
||||||
@@ -35,9 +33,3 @@ impl From<&'static str> for CustomError {
|
|||||||
CustomError::Static(value)
|
CustomError::Static(value)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<String> for CustomError {
|
|
||||||
fn from(value: String) -> Self {
|
|
||||||
CustomError::Text(value)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -24,10 +24,10 @@ pub(crate) fn record_event(event_type: EventType, input: OrgSource<'_>) {
|
|||||||
*db.entry(key).or_insert(0) += 1;
|
*db.entry(key).or_insert(0) += 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn report(original_document: &str) {
|
pub(crate) fn report(original_document: &str) {
|
||||||
let mut db = GLOBAL_DATA.lock().unwrap();
|
let mut db = GLOBAL_DATA.lock().unwrap();
|
||||||
let db = db.get_or_insert_with(HashMap::new);
|
let db = db.get_or_insert_with(HashMap::new);
|
||||||
let mut results: Vec<_> = db.iter().map(|(k, v)| (k, v)).collect();
|
let mut results: Vec<_> = db.iter().collect();
|
||||||
results.sort_by_key(|(_k, v)| *v);
|
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.
|
// 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));
|
// results.sort_by(|(_ak, av), (_bk, bv)| bv.cmp(av));
|
||||||
|
|||||||
@@ -2,5 +2,5 @@ mod database;
|
|||||||
mod event_type;
|
mod event_type;
|
||||||
|
|
||||||
pub(crate) use database::record_event;
|
pub(crate) use database::record_event;
|
||||||
pub use database::report;
|
pub(crate) use database::report;
|
||||||
pub(crate) use event_type::EventType;
|
pub(crate) use event_type::EventType;
|
||||||
|
|||||||
@@ -90,12 +90,11 @@ impl<'r, 's> Iterator for AllAstNodeIter<'r, 's> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'r, 's> IntoIterator for AstNode<'r, 's> {
|
impl<'r, 's> AstNode<'r, 's> {
|
||||||
type Item = AstNode<'r, 's>;
|
/// Iterate all AST nodes.
|
||||||
|
///
|
||||||
type IntoIter = AllAstNodeIter<'r, 's>;
|
/// This is different from the iter/into_iter functions which iterate a single level of the children. This iterates the entire tree including returning the root node itself.
|
||||||
|
pub fn iter_all_ast_nodes(self) -> AllAstNodeIter<'r, 's> {
|
||||||
fn into_iter(self) -> Self::IntoIter {
|
|
||||||
AllAstNodeIter {
|
AllAstNodeIter {
|
||||||
root: Some(self),
|
root: Some(self),
|
||||||
queue: VecDeque::new(),
|
queue: VecDeque::new(),
|
||||||
|
|||||||
13
src/lib.rs
13
src/lib.rs
@@ -3,6 +3,8 @@
|
|||||||
#![feature(path_file_prefix)]
|
#![feature(path_file_prefix)]
|
||||||
#![feature(is_sorted)]
|
#![feature(is_sorted)]
|
||||||
#![feature(test)]
|
#![feature(test)]
|
||||||
|
#![feature(iter_intersperse)]
|
||||||
|
#![feature(exact_size_is_empty)]
|
||||||
// TODO: #![warn(missing_docs)]
|
// TODO: #![warn(missing_docs)]
|
||||||
#![allow(clippy::bool_assert_comparison)] // Sometimes you want the long form because its easier to see at a glance.
|
#![allow(clippy::bool_assert_comparison)] // Sometimes you want the long form because its easier to see at a glance.
|
||||||
|
|
||||||
@@ -10,11 +12,20 @@ extern crate test;
|
|||||||
|
|
||||||
#[cfg(feature = "compare")]
|
#[cfg(feature = "compare")]
|
||||||
pub mod compare;
|
pub mod compare;
|
||||||
|
pub mod parse_cli;
|
||||||
|
#[cfg(any(feature = "compare", feature = "wasm", feature = "wasm_test"))]
|
||||||
|
mod util;
|
||||||
|
#[cfg(any(feature = "wasm", feature = "wasm_test"))]
|
||||||
|
mod wasm;
|
||||||
|
#[cfg(any(feature = "wasm", feature = "wasm_test"))]
|
||||||
|
pub mod wasm_cli;
|
||||||
|
#[cfg(feature = "wasm_test")]
|
||||||
|
pub mod wasm_test;
|
||||||
|
|
||||||
mod context;
|
mod context;
|
||||||
mod error;
|
mod error;
|
||||||
#[cfg(feature = "event_count")]
|
#[cfg(feature = "event_count")]
|
||||||
pub mod event_count;
|
mod event_count;
|
||||||
mod iter;
|
mod iter;
|
||||||
pub mod parser;
|
pub mod parser;
|
||||||
pub mod types;
|
pub mod types;
|
||||||
|
|||||||
62
src/main.rs
62
src/main.rs
@@ -1,12 +1,4 @@
|
|||||||
#![feature(round_char_boundary)]
|
use organic::parse_cli::main_body;
|
||||||
#![feature(exact_size_is_empty)]
|
|
||||||
use std::io::Read;
|
|
||||||
use std::path::Path;
|
|
||||||
|
|
||||||
use ::organic::parser::parse;
|
|
||||||
use organic::parser::parse_with_settings;
|
|
||||||
use organic::settings::GlobalSettings;
|
|
||||||
use organic::settings::LocalFileAccessInterface;
|
|
||||||
|
|
||||||
#[cfg(feature = "tracing")]
|
#[cfg(feature = "tracing")]
|
||||||
use crate::init_tracing::init_telemetry;
|
use crate::init_tracing::init_telemetry;
|
||||||
@@ -30,55 +22,3 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
|
|||||||
main_body_result
|
main_body_result
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
|
|
||||||
fn main_body() -> Result<(), Box<dyn std::error::Error>> {
|
|
||||||
let args = std::env::args().skip(1);
|
|
||||||
if args.is_empty() {
|
|
||||||
let org_contents = read_stdin_to_string()?;
|
|
||||||
run_anonymous_parse(org_contents)
|
|
||||||
} else {
|
|
||||||
for arg in args {
|
|
||||||
run_parse_on_file(arg)?
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn read_stdin_to_string() -> Result<String, Box<dyn std::error::Error>> {
|
|
||||||
let mut stdin_contents = String::new();
|
|
||||||
std::io::stdin()
|
|
||||||
.lock()
|
|
||||||
.read_to_string(&mut stdin_contents)?;
|
|
||||||
Ok(stdin_contents)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn run_anonymous_parse<P: AsRef<str>>(org_contents: P) -> Result<(), Box<dyn std::error::Error>> {
|
|
||||||
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(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn run_parse_on_file<P: AsRef<Path>>(org_path: P) -> Result<(), Box<dyn std::error::Error>> {
|
|
||||||
let org_path = org_path.as_ref();
|
|
||||||
let parent_directory = org_path
|
|
||||||
.parent()
|
|
||||||
.ok_or("Should be contained inside a directory.")?;
|
|
||||||
let org_contents = std::fs::read_to_string(org_path)?;
|
|
||||||
let org_contents = org_contents.as_str();
|
|
||||||
let file_access_interface = LocalFileAccessInterface {
|
|
||||||
working_directory: Some(parent_directory.to_path_buf()),
|
|
||||||
};
|
|
||||||
let global_settings = GlobalSettings {
|
|
||||||
file_access: &file_access_interface,
|
|
||||||
..Default::default()
|
|
||||||
};
|
|
||||||
let rust_parsed = parse_with_settings(org_contents, &global_settings)?;
|
|
||||||
println!("{:#?}", rust_parsed);
|
|
||||||
#[cfg(feature = "event_count")]
|
|
||||||
organic::event_count::report(org_contents);
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|||||||
59
src/parse_cli/mod.rs
Normal file
59
src/parse_cli/mod.rs
Normal file
@@ -0,0 +1,59 @@
|
|||||||
|
use std::io::Read;
|
||||||
|
use std::path::Path;
|
||||||
|
|
||||||
|
use crate::parser::parse;
|
||||||
|
use crate::parser::parse_with_settings;
|
||||||
|
use crate::settings::GlobalSettings;
|
||||||
|
use crate::settings::LocalFileAccessInterface;
|
||||||
|
|
||||||
|
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
|
||||||
|
pub fn main_body() -> Result<(), Box<dyn std::error::Error>> {
|
||||||
|
let args = std::env::args().skip(1);
|
||||||
|
if args.is_empty() {
|
||||||
|
let org_contents = read_stdin_to_string()?;
|
||||||
|
run_anonymous_parse(org_contents)
|
||||||
|
} else {
|
||||||
|
for arg in args {
|
||||||
|
run_parse_on_file(arg)?
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn read_stdin_to_string() -> Result<String, Box<dyn std::error::Error>> {
|
||||||
|
let mut stdin_contents = String::new();
|
||||||
|
std::io::stdin()
|
||||||
|
.lock()
|
||||||
|
.read_to_string(&mut stdin_contents)?;
|
||||||
|
Ok(stdin_contents)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn run_anonymous_parse<P: AsRef<str>>(org_contents: P) -> Result<(), Box<dyn std::error::Error>> {
|
||||||
|
let org_contents = org_contents.as_ref();
|
||||||
|
let rust_parsed = parse(org_contents)?;
|
||||||
|
println!("{:#?}", rust_parsed);
|
||||||
|
#[cfg(feature = "event_count")]
|
||||||
|
crate::event_count::report(org_contents);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn run_parse_on_file<P: AsRef<Path>>(org_path: P) -> Result<(), Box<dyn std::error::Error>> {
|
||||||
|
let org_path = org_path.as_ref();
|
||||||
|
let parent_directory = org_path
|
||||||
|
.parent()
|
||||||
|
.ok_or("Should be contained inside a directory.")?;
|
||||||
|
let org_contents = std::fs::read_to_string(org_path)?;
|
||||||
|
let org_contents = org_contents.as_str();
|
||||||
|
let file_access_interface = LocalFileAccessInterface {
|
||||||
|
working_directory: Some(parent_directory.to_path_buf()),
|
||||||
|
};
|
||||||
|
let global_settings = GlobalSettings {
|
||||||
|
file_access: &file_access_interface,
|
||||||
|
..Default::default()
|
||||||
|
};
|
||||||
|
let rust_parsed = parse_with_settings(org_contents, &global_settings)?;
|
||||||
|
println!("{:#?}", rust_parsed);
|
||||||
|
#[cfg(feature = "event_count")]
|
||||||
|
crate::event_count::report(org_contents);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
@@ -47,7 +47,7 @@ pub(crate) fn angle_link<'b, 'g, 'r, 's>(
|
|||||||
parser_with_context!(parse_angle_link)(context),
|
parser_with_context!(parse_angle_link)(context),
|
||||||
))(remaining)?;
|
))(remaining)?;
|
||||||
let (remaining, _) = tag(">")(remaining)?;
|
let (remaining, _) = tag(">")(remaining)?;
|
||||||
let (remaining, _trailing_whitespace) =
|
let (remaining, post_blank) =
|
||||||
maybe_consume_object_trailing_whitespace_if_not_exiting(context, remaining)?;
|
maybe_consume_object_trailing_whitespace_if_not_exiting(context, remaining)?;
|
||||||
let source = get_consumed(input, remaining);
|
let source = get_consumed(input, remaining);
|
||||||
Ok((
|
Ok((
|
||||||
@@ -59,6 +59,7 @@ pub(crate) fn angle_link<'b, 'g, 'r, 's>(
|
|||||||
raw_link: raw_link.into(),
|
raw_link: raw_link.into(),
|
||||||
search_option: parsed_link.search_option,
|
search_option: parsed_link.search_option,
|
||||||
application: parsed_link.application,
|
application: parsed_link.application,
|
||||||
|
post_blank: post_blank.map(Into::<&str>::into),
|
||||||
},
|
},
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,7 +4,6 @@ use nom::bytes::complete::tag_no_case;
|
|||||||
use nom::character::complete::anychar;
|
use nom::character::complete::anychar;
|
||||||
use nom::character::complete::one_of;
|
use nom::character::complete::one_of;
|
||||||
use nom::character::complete::space0;
|
use nom::character::complete::space0;
|
||||||
use nom::combinator::consumed;
|
|
||||||
use nom::combinator::opt;
|
use nom::combinator::opt;
|
||||||
use nom::combinator::peek;
|
use nom::combinator::peek;
|
||||||
use nom::combinator::recognize;
|
use nom::combinator::recognize;
|
||||||
@@ -43,32 +42,10 @@ where
|
|||||||
start_of_line(remaining)?;
|
start_of_line(remaining)?;
|
||||||
let (remaining, _) = tuple((space0, tag("#+"), tag_no_case("call"), tag(":")))(remaining)?;
|
let (remaining, _) = tuple((space0, tag("#+"), tag_no_case("call"), tag(":")))(remaining)?;
|
||||||
|
|
||||||
if let Ok((remaining, (_, line_break))) = tuple((space0, org_line_ending))(remaining) {
|
|
||||||
let (remaining, _trailing_ws) =
|
|
||||||
maybe_consume_trailing_whitespace_if_not_exiting(context, remaining)?;
|
|
||||||
let source = get_consumed(input, remaining);
|
|
||||||
return Ok((
|
|
||||||
remaining,
|
|
||||||
BabelCall {
|
|
||||||
source: Into::<&str>::into(source),
|
|
||||||
affiliated_keywords: parse_affiliated_keywords(
|
|
||||||
context.get_global_settings(),
|
|
||||||
affiliated_keywords,
|
|
||||||
),
|
|
||||||
value: Into::<&str>::into(line_break.take(0)),
|
|
||||||
call: None,
|
|
||||||
inside_header: None,
|
|
||||||
arguments: None,
|
|
||||||
end_header: None,
|
|
||||||
},
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
||||||
let (remaining, _ws) = space0(remaining)?;
|
let (remaining, _ws) = space0(remaining)?;
|
||||||
let (remaining, (value, babel_call_value)) = consumed(babel_call_value)(remaining)?;
|
let (remaining, babel_call_value) = babel_call_value(remaining)?;
|
||||||
let (remaining, _ws) = tuple((space0, org_line_ending))(remaining)?;
|
|
||||||
|
|
||||||
let (remaining, _trailing_ws) =
|
let (remaining, post_blank) =
|
||||||
maybe_consume_trailing_whitespace_if_not_exiting(context, remaining)?;
|
maybe_consume_trailing_whitespace_if_not_exiting(context, remaining)?;
|
||||||
let source = get_consumed(input, remaining);
|
let source = get_consumed(input, remaining);
|
||||||
|
|
||||||
@@ -80,17 +57,22 @@ where
|
|||||||
context.get_global_settings(),
|
context.get_global_settings(),
|
||||||
affiliated_keywords,
|
affiliated_keywords,
|
||||||
),
|
),
|
||||||
value: Into::<&str>::into(value).trim_end(),
|
value: Into::<&str>::into(babel_call_value.value),
|
||||||
call: babel_call_value.call.map(Into::<&str>::into),
|
call: babel_call_value.call.map(Into::<&str>::into),
|
||||||
inside_header: babel_call_value.inside_header.map(Into::<&str>::into),
|
inside_header: babel_call_value.inside_header.map(Into::<&str>::into),
|
||||||
arguments: babel_call_value.arguments.map(Into::<&str>::into),
|
arguments: babel_call_value.arguments.map(Into::<&str>::into),
|
||||||
end_header: babel_call_value.end_header.map(Into::<&str>::into),
|
end_header: babel_call_value.end_header.map(Into::<&str>::into),
|
||||||
|
post_blank: post_blank.map(Into::<&str>::into),
|
||||||
},
|
},
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
struct BabelCallValue<'s> {
|
struct BabelCallValue<'s> {
|
||||||
|
/// The entire string to the right of "#+call: " without the trailing line break.
|
||||||
|
value: OrgSource<'s>,
|
||||||
|
|
||||||
|
/// The function name which may contain a line break if there are no headers/arguments.
|
||||||
call: Option<OrgSource<'s>>,
|
call: Option<OrgSource<'s>>,
|
||||||
inside_header: Option<OrgSource<'s>>,
|
inside_header: Option<OrgSource<'s>>,
|
||||||
arguments: Option<OrgSource<'s>>,
|
arguments: Option<OrgSource<'s>>,
|
||||||
@@ -99,13 +81,45 @@ struct BabelCallValue<'s> {
|
|||||||
|
|
||||||
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
|
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
|
||||||
fn babel_call_value<'s>(input: OrgSource<'s>) -> Res<OrgSource<'s>, BabelCallValue<'s>> {
|
fn babel_call_value<'s>(input: OrgSource<'s>) -> Res<OrgSource<'s>, BabelCallValue<'s>> {
|
||||||
let (remaining, call) = opt(babel_call_call)(input)?;
|
alt((
|
||||||
let (remaining, inside_header) = opt(inside_header)(remaining)?;
|
babel_call_value_without_headers,
|
||||||
let (remaining, arguments) = opt(arguments)(remaining)?;
|
babel_call_value_with_headers,
|
||||||
let (remaining, end_header) = opt(end_header)(remaining)?;
|
))(input)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
|
||||||
|
fn babel_call_value_without_headers<'s>(
|
||||||
|
input: OrgSource<'s>,
|
||||||
|
) -> Res<OrgSource<'s>, BabelCallValue<'s>> {
|
||||||
|
let (remaining, value) = babel_call_call_with_headers(input)?;
|
||||||
|
let (remaining, _ws) = tuple((space0, org_line_ending))(remaining)?;
|
||||||
|
let call = get_consumed(input, remaining);
|
||||||
Ok((
|
Ok((
|
||||||
remaining,
|
remaining,
|
||||||
BabelCallValue {
|
BabelCallValue {
|
||||||
|
value,
|
||||||
|
call: Some(call),
|
||||||
|
inside_header: None,
|
||||||
|
arguments: None,
|
||||||
|
end_header: None,
|
||||||
|
},
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
|
||||||
|
fn babel_call_value_with_headers<'s>(
|
||||||
|
input: OrgSource<'s>,
|
||||||
|
) -> Res<OrgSource<'s>, BabelCallValue<'s>> {
|
||||||
|
let (remaining, call) = opt(babel_call_call_with_headers)(input)?;
|
||||||
|
let (remaining, inside_header) = opt(inside_header)(remaining)?;
|
||||||
|
let (remaining, arguments) = opt(arguments)(remaining)?;
|
||||||
|
let (remaining, end_header) = opt(end_header)(remaining)?;
|
||||||
|
let value = get_consumed(input, remaining);
|
||||||
|
let (remaining, _ws) = tuple((space0, org_line_ending))(remaining)?;
|
||||||
|
Ok((
|
||||||
|
remaining,
|
||||||
|
BabelCallValue {
|
||||||
|
value,
|
||||||
call,
|
call,
|
||||||
inside_header,
|
inside_header,
|
||||||
arguments: arguments.flatten(),
|
arguments: arguments.flatten(),
|
||||||
@@ -115,14 +129,15 @@ fn babel_call_value<'s>(input: OrgSource<'s>) -> Res<OrgSource<'s>, BabelCallVal
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
|
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
|
||||||
fn babel_call_call<'s>(input: OrgSource<'s>) -> Res<OrgSource<'s>, OrgSource<'s>> {
|
fn babel_call_call_with_headers<'s>(input: OrgSource<'s>) -> Res<OrgSource<'s>, OrgSource<'s>> {
|
||||||
|
// When babel call contains no arguments or headers (for example: "#+call: lorem ipsum\n") then the trailing line break is part of the call. Otherwise, it is not.
|
||||||
verify(
|
verify(
|
||||||
recognize(many_till(
|
recognize(many_till(
|
||||||
anychar,
|
anychar,
|
||||||
alt((
|
peek(alt((
|
||||||
peek(recognize(one_of("[("))),
|
recognize(one_of("[(")),
|
||||||
recognize(tuple((space0, org_line_ending))),
|
recognize(tuple((space0, org_line_ending))),
|
||||||
)),
|
))),
|
||||||
)),
|
)),
|
||||||
|s| s.len() > 0,
|
|s| s.len() > 0,
|
||||||
)(input)
|
)(input)
|
||||||
@@ -225,26 +240,10 @@ fn impl_balanced_bracket<
|
|||||||
let contents_end = remaining;
|
let contents_end = remaining;
|
||||||
|
|
||||||
let (remaining, _) = end_parser(remaining)?;
|
let (remaining, _) = end_parser(remaining)?;
|
||||||
let contents = if contents_start != contents_end {
|
let contents = if Into::<&str>::into(contents_start) != Into::<&str>::into(contents_end) {
|
||||||
Some(contents_start.get_until(contents_end))
|
Some(contents_start.get_until(contents_end))
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
};
|
};
|
||||||
Ok((remaining, contents))
|
Ok((remaining, contents))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod tests {
|
|
||||||
use nom::combinator::opt;
|
|
||||||
|
|
||||||
use super::*;
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn simple_call() -> Result<(), Box<dyn std::error::Error>> {
|
|
||||||
let input = OrgSource::new("()");
|
|
||||||
let (remaining, call) = opt(babel_call_call)(input)?;
|
|
||||||
assert_eq!(Into::<&str>::into(remaining), "()");
|
|
||||||
assert_eq!(call, None);
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -15,10 +15,8 @@ use crate::context::RefContext;
|
|||||||
use crate::error::CustomError;
|
use crate::error::CustomError;
|
||||||
use crate::error::Res;
|
use crate::error::Res;
|
||||||
use crate::parser::macros::element;
|
use crate::parser::macros::element;
|
||||||
use crate::types::AffiliatedKeywords;
|
|
||||||
use crate::types::Object;
|
use crate::types::Object;
|
||||||
use crate::types::Paragraph;
|
use crate::types::Paragraph;
|
||||||
use crate::types::PlainText;
|
|
||||||
|
|
||||||
#[cfg_attr(
|
#[cfg_attr(
|
||||||
feature = "tracing",
|
feature = "tracing",
|
||||||
@@ -65,6 +63,7 @@ pub(crate) fn broken_end<'b, 'g, 'r, 's>(
|
|||||||
match paragraph.children.first_mut() {
|
match paragraph.children.first_mut() {
|
||||||
Some(Object::PlainText(plain_text)) => {
|
Some(Object::PlainText(plain_text)) => {
|
||||||
plain_text.source = input.get_until_end_of_str(plain_text.source).into();
|
plain_text.source = input.get_until_end_of_str(plain_text.source).into();
|
||||||
|
paragraph.contents = Some(input.get_until_end_of_str(plain_text.source).into());
|
||||||
}
|
}
|
||||||
Some(obj) => {
|
Some(obj) => {
|
||||||
panic!("Unhandled first object type inside bullshitium {:?}", obj);
|
panic!("Unhandled first object type inside bullshitium {:?}", obj);
|
||||||
@@ -75,18 +74,19 @@ pub(crate) fn broken_end<'b, 'g, 'r, 's>(
|
|||||||
};
|
};
|
||||||
Ok((remaining, paragraph))
|
Ok((remaining, paragraph))
|
||||||
} else {
|
} else {
|
||||||
let (remaining, _trailing_ws) =
|
let (remaining, post_blank) =
|
||||||
maybe_consume_trailing_whitespace_if_not_exiting(context, lead_in_remaining)?;
|
maybe_consume_trailing_whitespace_if_not_exiting(context, lead_in_remaining)?;
|
||||||
|
|
||||||
|
let body = Into::<&str>::into(input.get_until(lead_in_remaining));
|
||||||
|
|
||||||
Ok((
|
Ok((
|
||||||
remaining,
|
remaining,
|
||||||
Paragraph {
|
Paragraph::of_text(
|
||||||
source: input.get_until(remaining).into(),
|
input.get_until(remaining).into(),
|
||||||
affiliated_keywords: AffiliatedKeywords::default(),
|
body,
|
||||||
children: vec![Object::PlainText(PlainText {
|
if !body.is_empty() { Some(body) } else { None },
|
||||||
source: input.get_until(lead_in_remaining).into(),
|
post_blank.map(Into::<&str>::into),
|
||||||
})],
|
),
|
||||||
},
|
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -124,6 +124,7 @@ pub(crate) fn broken_dynamic_block<'b, 'g, 'r, 's>(
|
|||||||
match paragraph.children.first_mut() {
|
match paragraph.children.first_mut() {
|
||||||
Some(Object::PlainText(plain_text)) => {
|
Some(Object::PlainText(plain_text)) => {
|
||||||
plain_text.source = input.get_until_end_of_str(plain_text.source).into();
|
plain_text.source = input.get_until_end_of_str(plain_text.source).into();
|
||||||
|
paragraph.contents = Some(input.get_until_end_of_str(plain_text.source).into());
|
||||||
}
|
}
|
||||||
Some(obj) => {
|
Some(obj) => {
|
||||||
panic!("Unhandled first object type inside bullshitium {:?}", obj);
|
panic!("Unhandled first object type inside bullshitium {:?}", obj);
|
||||||
@@ -134,18 +135,19 @@ pub(crate) fn broken_dynamic_block<'b, 'g, 'r, 's>(
|
|||||||
};
|
};
|
||||||
Ok((remaining, paragraph))
|
Ok((remaining, paragraph))
|
||||||
} else {
|
} else {
|
||||||
let (remaining, _trailing_ws) =
|
let (remaining, post_blank) =
|
||||||
maybe_consume_trailing_whitespace_if_not_exiting(context, lead_in_remaining)?;
|
maybe_consume_trailing_whitespace_if_not_exiting(context, lead_in_remaining)?;
|
||||||
|
|
||||||
|
let body = Into::<&str>::into(input.get_until(lead_in_remaining));
|
||||||
|
|
||||||
Ok((
|
Ok((
|
||||||
remaining,
|
remaining,
|
||||||
Paragraph {
|
Paragraph::of_text(
|
||||||
source: input.get_until(remaining).into(),
|
input.get_until(remaining).into(),
|
||||||
affiliated_keywords: AffiliatedKeywords::default(),
|
body,
|
||||||
children: vec![Object::PlainText(PlainText {
|
if !body.is_empty() { Some(body) } else { None },
|
||||||
source: input.get_until(lead_in_remaining).into(),
|
post_blank.map(Into::<&str>::into),
|
||||||
})],
|
),
|
||||||
},
|
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -46,16 +46,22 @@ pub(crate) fn citation<'b, 'g, 'r, 's>(
|
|||||||
let (remaining, prefix) =
|
let (remaining, prefix) =
|
||||||
must_balance_bracket(opt(parser_with_context!(global_prefix)(context)))(remaining)?;
|
must_balance_bracket(opt(parser_with_context!(global_prefix)(context)))(remaining)?;
|
||||||
|
|
||||||
|
let contents_begin = remaining;
|
||||||
let (remaining, references) =
|
let (remaining, references) =
|
||||||
separated_list1(tag(";"), parser_with_context!(citation_reference)(context))(remaining)?;
|
separated_list1(tag(";"), parser_with_context!(citation_reference)(context))(remaining)?;
|
||||||
|
let contents_end = {
|
||||||
|
let (rem, _) = opt(tag(";"))(remaining)?;
|
||||||
|
rem
|
||||||
|
};
|
||||||
let (remaining, suffix) = must_balance_bracket(opt(map(
|
let (remaining, suffix) = must_balance_bracket(opt(map(
|
||||||
tuple((tag(";"), parser_with_context!(global_suffix)(context))),
|
tuple((tag(";"), parser_with_context!(global_suffix)(context))),
|
||||||
|(_, suffix)| suffix,
|
|(_, suffix)| suffix,
|
||||||
)))(remaining)?;
|
)))(remaining)?;
|
||||||
let (remaining, _) = tag("]")(remaining)?;
|
let (remaining, _) = tag("]")(remaining)?;
|
||||||
let (remaining, _trailing_whitespace) =
|
let (remaining, post_blank) =
|
||||||
maybe_consume_object_trailing_whitespace_if_not_exiting(context, remaining)?;
|
maybe_consume_object_trailing_whitespace_if_not_exiting(context, remaining)?;
|
||||||
let source = get_consumed(input, remaining);
|
let source = get_consumed(input, remaining);
|
||||||
|
let contents = contents_begin.get_until(contents_end);
|
||||||
Ok((
|
Ok((
|
||||||
remaining,
|
remaining,
|
||||||
Citation {
|
Citation {
|
||||||
@@ -64,6 +70,8 @@ pub(crate) fn citation<'b, 'g, 'r, 's>(
|
|||||||
prefix: prefix.unwrap_or(Vec::new()),
|
prefix: prefix.unwrap_or(Vec::new()),
|
||||||
suffix: suffix.unwrap_or(Vec::new()),
|
suffix: suffix.unwrap_or(Vec::new()),
|
||||||
children: references,
|
children: references,
|
||||||
|
contents: Into::<&str>::into(contents),
|
||||||
|
post_blank: post_blank.map(Into::<&str>::into),
|
||||||
},
|
},
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
@@ -210,12 +218,11 @@ mod tests {
|
|||||||
use crate::context::GlobalSettings;
|
use crate::context::GlobalSettings;
|
||||||
use crate::context::List;
|
use crate::context::List;
|
||||||
use crate::parser::element_parser::element;
|
use crate::parser::element_parser::element;
|
||||||
use crate::types::CitationReference;
|
|
||||||
use crate::types::Element;
|
use crate::types::Element;
|
||||||
use crate::types::GetStandardProperties;
|
use crate::types::StandardProperties;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn citation_simple() {
|
fn citation_simple() -> Result<(), Box<dyn std::error::Error>> {
|
||||||
let input = OrgSource::new("[cite:@foo]");
|
let input = OrgSource::new("[cite:@foo]");
|
||||||
let global_settings = GlobalSettings::default();
|
let global_settings = GlobalSettings::default();
|
||||||
let initial_context = ContextElement::document_context();
|
let initial_context = ContextElement::document_context();
|
||||||
@@ -227,28 +234,33 @@ mod tests {
|
|||||||
_ => panic!("Should be a paragraph!"),
|
_ => panic!("Should be a paragraph!"),
|
||||||
};
|
};
|
||||||
assert_eq!(Into::<&str>::into(remaining), "");
|
assert_eq!(Into::<&str>::into(remaining), "");
|
||||||
assert_eq!(
|
assert_eq!(first_paragraph.get_source(), "[cite:@foo]");
|
||||||
first_paragraph.get_standard_properties().get_source(),
|
|
||||||
"[cite:@foo]"
|
|
||||||
);
|
|
||||||
assert_eq!(first_paragraph.children.len(), 1);
|
assert_eq!(first_paragraph.children.len(), 1);
|
||||||
assert_eq!(
|
|
||||||
first_paragraph
|
match first_paragraph
|
||||||
.children
|
.children
|
||||||
.get(0)
|
.first()
|
||||||
.expect("Len already asserted to be 1"),
|
.expect("Len already asserted to be 1.")
|
||||||
&Object::Citation(Citation {
|
{
|
||||||
source: "[cite:@foo]",
|
Object::Citation(inner) => {
|
||||||
style: None,
|
assert_eq!(inner.get_source(), "[cite:@foo]");
|
||||||
prefix: vec![],
|
assert_eq!(inner.children.len(), 1);
|
||||||
suffix: vec![],
|
assert!(inner.prefix.is_empty());
|
||||||
children: vec![CitationReference {
|
assert!(inner.suffix.is_empty());
|
||||||
source: "@foo",
|
assert!(inner.style.is_none());
|
||||||
key: "foo",
|
let citation_reference = inner
|
||||||
prefix: vec![],
|
.children
|
||||||
suffix: vec![]
|
.first()
|
||||||
}]
|
.expect("Len already asserted to be 1.");
|
||||||
})
|
assert_eq!(citation_reference.get_source(), "@foo");
|
||||||
);
|
assert_eq!(citation_reference.key, "foo");
|
||||||
|
assert!(citation_reference.prefix.is_empty());
|
||||||
|
assert!(citation_reference.suffix.is_empty());
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
return Err("Child should be a citation.".into());
|
||||||
|
}
|
||||||
|
};
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -40,7 +40,7 @@ pub(crate) fn clock<'b, 'g, 'r, 's>(
|
|||||||
let (remaining, (timestamp, duration)) = clock_timestamp(context, remaining)?;
|
let (remaining, (timestamp, duration)) = clock_timestamp(context, remaining)?;
|
||||||
let (remaining, _) = tuple((space0, org_line_ending))(remaining)?;
|
let (remaining, _) = tuple((space0, org_line_ending))(remaining)?;
|
||||||
|
|
||||||
let (remaining, _trailing_ws) =
|
let (remaining, post_blank) =
|
||||||
maybe_consume_trailing_whitespace_if_not_exiting(context, remaining)?;
|
maybe_consume_trailing_whitespace_if_not_exiting(context, remaining)?;
|
||||||
let source = get_consumed(input, remaining);
|
let source = get_consumed(input, remaining);
|
||||||
Ok((
|
Ok((
|
||||||
@@ -54,6 +54,7 @@ pub(crate) fn clock<'b, 'g, 'r, 's>(
|
|||||||
} else {
|
} else {
|
||||||
ClockStatus::Running
|
ClockStatus::Running
|
||||||
},
|
},
|
||||||
|
post_blank: post_blank.map(Into::<&str>::into),
|
||||||
},
|
},
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
@@ -81,7 +82,7 @@ fn clock_timestamp<'b, 'g, 'r, 's>(
|
|||||||
|(timestamp, duration)| (timestamp, duration.map(Into::<&str>::into)),
|
|(timestamp, duration)| (timestamp, duration.map(Into::<&str>::into)),
|
||||||
),
|
),
|
||||||
map(
|
map(
|
||||||
parser_with_context!(inactive_timestamp)(context),
|
parser_with_context!(inactive_timestamp(true))(context),
|
||||||
|timestamp| (timestamp, None),
|
|timestamp| (timestamp, None),
|
||||||
),
|
),
|
||||||
))(input)
|
))(input)
|
||||||
|
|||||||
@@ -46,7 +46,7 @@ pub(crate) fn comment<'b, 'g, 'r, 's>(
|
|||||||
let (remaining, mut remaining_lines) =
|
let (remaining, mut remaining_lines) =
|
||||||
many0(preceded(not(exit_matcher), comment_line_matcher))(remaining)?;
|
many0(preceded(not(exit_matcher), comment_line_matcher))(remaining)?;
|
||||||
|
|
||||||
let (remaining, _trailing_ws) =
|
let (remaining, post_blank) =
|
||||||
maybe_consume_trailing_whitespace_if_not_exiting(context, remaining)?;
|
maybe_consume_trailing_whitespace_if_not_exiting(context, remaining)?;
|
||||||
let source = get_consumed(input, remaining);
|
let source = get_consumed(input, remaining);
|
||||||
let mut value = Vec::with_capacity(remaining_lines.len() + 1);
|
let mut value = Vec::with_capacity(remaining_lines.len() + 1);
|
||||||
@@ -67,6 +67,7 @@ pub(crate) fn comment<'b, 'g, 'r, 's>(
|
|||||||
Comment {
|
Comment {
|
||||||
source: source.into(),
|
source: source.into(),
|
||||||
value,
|
value,
|
||||||
|
post_blank: post_blank.map(Into::<&str>::into),
|
||||||
},
|
},
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -31,7 +31,7 @@ where
|
|||||||
let (remaining, value) = recognize(tuple((tag("%%("), is_not("\r\n"))))(remaining)?;
|
let (remaining, value) = recognize(tuple((tag("%%("), is_not("\r\n"))))(remaining)?;
|
||||||
let (remaining, _eol) = org_line_ending(remaining)?;
|
let (remaining, _eol) = org_line_ending(remaining)?;
|
||||||
|
|
||||||
let (remaining, _trailing_ws) =
|
let (remaining, post_blank) =
|
||||||
maybe_consume_trailing_whitespace_if_not_exiting(context, remaining)?;
|
maybe_consume_trailing_whitespace_if_not_exiting(context, remaining)?;
|
||||||
let source = get_consumed(input, remaining);
|
let source = get_consumed(input, remaining);
|
||||||
Ok((
|
Ok((
|
||||||
@@ -43,6 +43,7 @@ where
|
|||||||
affiliated_keywords,
|
affiliated_keywords,
|
||||||
),
|
),
|
||||||
value: Into::<&str>::into(value),
|
value: Into::<&str>::into(value),
|
||||||
|
post_blank: post_blank.map(Into::<&str>::into),
|
||||||
},
|
},
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ use std::path::Path;
|
|||||||
use nom::combinator::all_consuming;
|
use nom::combinator::all_consuming;
|
||||||
use nom::combinator::opt;
|
use nom::combinator::opt;
|
||||||
use nom::multi::many0;
|
use nom::multi::many0;
|
||||||
|
use nom::InputTake;
|
||||||
|
|
||||||
use super::headline::heading;
|
use super::headline::heading;
|
||||||
use super::in_buffer_settings::apply_in_buffer_settings;
|
use super::in_buffer_settings::apply_in_buffer_settings;
|
||||||
@@ -143,7 +144,7 @@ fn document_org_source<'b, 'g, 'r, 's>(
|
|||||||
{
|
{
|
||||||
// If there are radio targets in this document then we need to parse the entire document again with the knowledge of the radio targets.
|
// If there are radio targets in this document then we need to parse the entire document again with the knowledge of the radio targets.
|
||||||
let all_radio_targets: Vec<&Vec<Object<'_>>> = Into::<AstNode>::into(&document)
|
let all_radio_targets: Vec<&Vec<Object<'_>>> = Into::<AstNode>::into(&document)
|
||||||
.into_iter()
|
.iter_all_ast_nodes()
|
||||||
.filter_map(|ast_node| {
|
.filter_map(|ast_node| {
|
||||||
if let AstNode::RadioTarget(ast_node) = ast_node {
|
if let AstNode::RadioTarget(ast_node) = ast_node {
|
||||||
Some(ast_node)
|
Some(ast_node)
|
||||||
@@ -181,8 +182,10 @@ fn _document<'b, 'g, 'r, 's>(
|
|||||||
let zeroth_section_matcher = parser_with_context!(zeroth_section)(context);
|
let zeroth_section_matcher = parser_with_context!(zeroth_section)(context);
|
||||||
let heading_matcher = parser_with_context!(heading(0))(context);
|
let heading_matcher = parser_with_context!(heading(0))(context);
|
||||||
let (remaining, _blank_lines) = many0(blank_line)(input)?;
|
let (remaining, _blank_lines) = many0(blank_line)(input)?;
|
||||||
|
let contents_begin = remaining;
|
||||||
let (remaining, zeroth_section) = opt(zeroth_section_matcher)(remaining)?;
|
let (remaining, zeroth_section) = opt(zeroth_section_matcher)(remaining)?;
|
||||||
let (remaining, children) = many0(heading_matcher)(remaining)?;
|
let (remaining, children) = many0(heading_matcher)(remaining)?;
|
||||||
|
let contents = get_consumed(contents_begin, remaining);
|
||||||
let source = get_consumed(input, remaining);
|
let source = get_consumed(input, remaining);
|
||||||
Ok((
|
Ok((
|
||||||
remaining,
|
remaining,
|
||||||
@@ -192,6 +195,11 @@ fn _document<'b, 'g, 'r, 's>(
|
|||||||
path: None,
|
path: None,
|
||||||
zeroth_section,
|
zeroth_section,
|
||||||
children,
|
children,
|
||||||
|
contents: if contents.len() > 0 {
|
||||||
|
Into::<&str>::into(contents)
|
||||||
|
} else {
|
||||||
|
Into::<&str>::into(remaining.take(0))
|
||||||
|
},
|
||||||
},
|
},
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ use nom::bytes::complete::tag_no_case;
|
|||||||
use nom::bytes::complete::take_while;
|
use nom::bytes::complete::take_while;
|
||||||
use nom::character::complete::line_ending;
|
use nom::character::complete::line_ending;
|
||||||
use nom::character::complete::space0;
|
use nom::character::complete::space0;
|
||||||
|
use nom::combinator::consumed;
|
||||||
use nom::combinator::eof;
|
use nom::combinator::eof;
|
||||||
use nom::combinator::not;
|
use nom::combinator::not;
|
||||||
use nom::combinator::recognize;
|
use nom::combinator::recognize;
|
||||||
@@ -12,7 +13,9 @@ use nom::sequence::tuple;
|
|||||||
|
|
||||||
use super::affiliated_keyword::parse_affiliated_keywords;
|
use super::affiliated_keyword::parse_affiliated_keywords;
|
||||||
use super::org_source::OrgSource;
|
use super::org_source::OrgSource;
|
||||||
|
use super::paragraph::empty_paragraph;
|
||||||
use super::util::maybe_consume_trailing_whitespace_if_not_exiting;
|
use super::util::maybe_consume_trailing_whitespace_if_not_exiting;
|
||||||
|
use crate::context::bind_context;
|
||||||
use crate::context::parser_with_context;
|
use crate::context::parser_with_context;
|
||||||
use crate::context::ContextElement;
|
use crate::context::ContextElement;
|
||||||
use crate::context::ExitClass;
|
use crate::context::ExitClass;
|
||||||
@@ -21,7 +24,6 @@ use crate::context::RefContext;
|
|||||||
use crate::error::CustomError;
|
use crate::error::CustomError;
|
||||||
use crate::error::Res;
|
use crate::error::Res;
|
||||||
use crate::parser::element_parser::element;
|
use crate::parser::element_parser::element;
|
||||||
use crate::parser::util::blank_line;
|
|
||||||
use crate::parser::util::exit_matcher_parser;
|
use crate::parser::util::exit_matcher_parser;
|
||||||
use crate::parser::util::get_consumed;
|
use crate::parser::util::get_consumed;
|
||||||
use crate::parser::util::immediate_in_section;
|
use crate::parser::util::immediate_in_section;
|
||||||
@@ -30,8 +32,6 @@ use crate::parser::util::WORD_CONSTITUENT_CHARACTERS;
|
|||||||
use crate::types::Drawer;
|
use crate::types::Drawer;
|
||||||
use crate::types::Element;
|
use crate::types::Element;
|
||||||
use crate::types::Keyword;
|
use crate::types::Keyword;
|
||||||
use crate::types::Paragraph;
|
|
||||||
use crate::types::SetSource;
|
|
||||||
|
|
||||||
#[cfg_attr(
|
#[cfg_attr(
|
||||||
feature = "tracing",
|
feature = "tracing",
|
||||||
@@ -71,30 +71,12 @@ where
|
|||||||
let parser_context = context.with_additional_node(&contexts[0]);
|
let parser_context = context.with_additional_node(&contexts[0]);
|
||||||
let parser_context = parser_context.with_additional_node(&contexts[1]);
|
let parser_context = parser_context.with_additional_node(&contexts[1]);
|
||||||
let parser_context = parser_context.with_additional_node(&contexts[2]);
|
let parser_context = parser_context.with_additional_node(&contexts[2]);
|
||||||
|
let (remaining, (contents, children)) =
|
||||||
|
consumed(parser_with_context!(children)(&parser_context))(remaining)?;
|
||||||
|
|
||||||
let element_matcher = parser_with_context!(element(true))(&parser_context);
|
|
||||||
let exit_matcher = parser_with_context!(exit_matcher_parser)(&parser_context);
|
|
||||||
let (remaining, children) = match tuple((
|
|
||||||
not(exit_matcher),
|
|
||||||
blank_line,
|
|
||||||
many_till(blank_line, exit_matcher),
|
|
||||||
))(remaining)
|
|
||||||
{
|
|
||||||
Ok((remain, (_not_immediate_exit, first_line, (_trailing_whitespace, _exit_contents)))) => {
|
|
||||||
let mut element = Element::Paragraph(Paragraph::of_text(first_line.into()));
|
|
||||||
let source = get_consumed(remaining, remain);
|
|
||||||
element.set_source(source.into());
|
|
||||||
(remain, vec![element])
|
|
||||||
}
|
|
||||||
Err(_) => {
|
|
||||||
let (remaining, (children, _exit_contents)) =
|
|
||||||
many_till(element_matcher, exit_matcher)(remaining)?;
|
|
||||||
(remaining, children)
|
|
||||||
}
|
|
||||||
};
|
|
||||||
let (remaining, _end) = drawer_end(&parser_context, remaining)?;
|
let (remaining, _end) = drawer_end(&parser_context, remaining)?;
|
||||||
|
|
||||||
let (remaining, _trailing_ws) =
|
let (remaining, post_blank) =
|
||||||
maybe_consume_trailing_whitespace_if_not_exiting(context, remaining)?;
|
maybe_consume_trailing_whitespace_if_not_exiting(context, remaining)?;
|
||||||
let source = get_consumed(input, remaining);
|
let source = get_consumed(input, remaining);
|
||||||
|
|
||||||
@@ -108,10 +90,34 @@ where
|
|||||||
),
|
),
|
||||||
drawer_name: drawer_name.into(),
|
drawer_name: drawer_name.into(),
|
||||||
children,
|
children,
|
||||||
|
contents: Some(contents.into()),
|
||||||
|
post_blank: post_blank.map(Into::<&str>::into),
|
||||||
},
|
},
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg_attr(
|
||||||
|
feature = "tracing",
|
||||||
|
tracing::instrument(ret, level = "debug", skip(context))
|
||||||
|
)]
|
||||||
|
fn children<'b, 'g, 'r, 's>(
|
||||||
|
context: RefContext<'b, 'g, 'r, 's>,
|
||||||
|
input: OrgSource<'s>,
|
||||||
|
) -> Res<OrgSource<'s>, Vec<Element<'s>>> {
|
||||||
|
let element_matcher = parser_with_context!(element(true))(context);
|
||||||
|
let exit_matcher = parser_with_context!(exit_matcher_parser)(context);
|
||||||
|
|
||||||
|
if let Ok((remaining, (_not_exit, empty_para))) =
|
||||||
|
tuple((not(exit_matcher), bind_context!(empty_paragraph, context)))(input)
|
||||||
|
{
|
||||||
|
return Ok((remaining, vec![Element::Paragraph(empty_para)]));
|
||||||
|
}
|
||||||
|
|
||||||
|
let (remaining, (children, _exit_contents)) = many_till(element_matcher, exit_matcher)(input)?;
|
||||||
|
|
||||||
|
Ok((remaining, children))
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
|
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
|
||||||
fn name<'s>(input: OrgSource<'s>) -> Res<OrgSource<'s>, OrgSource<'s>> {
|
fn name<'s>(input: OrgSource<'s>) -> Res<OrgSource<'s>, OrgSource<'s>> {
|
||||||
take_while(|c| WORD_CONSTITUENT_CHARACTERS.contains(c) || "-_".contains(c))(input)
|
take_while(|c| WORD_CONSTITUENT_CHARACTERS.contains(c) || "-_".contains(c))(input)
|
||||||
|
|||||||
@@ -6,20 +6,20 @@ use nom::character::complete::anychar;
|
|||||||
use nom::character::complete::line_ending;
|
use nom::character::complete::line_ending;
|
||||||
use nom::character::complete::space0;
|
use nom::character::complete::space0;
|
||||||
use nom::character::complete::space1;
|
use nom::character::complete::space1;
|
||||||
use nom::combinator::consumed;
|
|
||||||
use nom::combinator::eof;
|
use nom::combinator::eof;
|
||||||
use nom::combinator::not;
|
use nom::combinator::not;
|
||||||
use nom::combinator::opt;
|
use nom::combinator::opt;
|
||||||
use nom::combinator::peek;
|
use nom::combinator::peek;
|
||||||
use nom::combinator::recognize;
|
use nom::combinator::recognize;
|
||||||
use nom::multi::many0;
|
|
||||||
use nom::multi::many_till;
|
use nom::multi::many_till;
|
||||||
use nom::sequence::preceded;
|
|
||||||
use nom::sequence::tuple;
|
use nom::sequence::tuple;
|
||||||
|
|
||||||
use super::affiliated_keyword::parse_affiliated_keywords;
|
use super::affiliated_keyword::parse_affiliated_keywords;
|
||||||
|
use super::greater_block::leading_blank_lines_end;
|
||||||
use super::org_source::OrgSource;
|
use super::org_source::OrgSource;
|
||||||
|
use super::paragraph::empty_paragraph;
|
||||||
use super::util::maybe_consume_trailing_whitespace_if_not_exiting;
|
use super::util::maybe_consume_trailing_whitespace_if_not_exiting;
|
||||||
|
use crate::context::bind_context;
|
||||||
use crate::context::parser_with_context;
|
use crate::context::parser_with_context;
|
||||||
use crate::context::ContextElement;
|
use crate::context::ContextElement;
|
||||||
use crate::context::ExitClass;
|
use crate::context::ExitClass;
|
||||||
@@ -28,7 +28,6 @@ use crate::context::RefContext;
|
|||||||
use crate::error::CustomError;
|
use crate::error::CustomError;
|
||||||
use crate::error::Res;
|
use crate::error::Res;
|
||||||
use crate::parser::element_parser::element;
|
use crate::parser::element_parser::element;
|
||||||
use crate::parser::util::blank_line;
|
|
||||||
use crate::parser::util::exit_matcher_parser;
|
use crate::parser::util::exit_matcher_parser;
|
||||||
use crate::parser::util::get_consumed;
|
use crate::parser::util::get_consumed;
|
||||||
use crate::parser::util::immediate_in_section;
|
use crate::parser::util::immediate_in_section;
|
||||||
@@ -36,8 +35,6 @@ use crate::parser::util::start_of_line;
|
|||||||
use crate::types::DynamicBlock;
|
use crate::types::DynamicBlock;
|
||||||
use crate::types::Element;
|
use crate::types::Element;
|
||||||
use crate::types::Keyword;
|
use crate::types::Keyword;
|
||||||
use crate::types::Paragraph;
|
|
||||||
use crate::types::SetSource;
|
|
||||||
|
|
||||||
#[cfg_attr(
|
#[cfg_attr(
|
||||||
feature = "tracing",
|
feature = "tracing",
|
||||||
@@ -82,25 +79,25 @@ where
|
|||||||
let element_matcher = parser_with_context!(element(true))(&parser_context);
|
let element_matcher = parser_with_context!(element(true))(&parser_context);
|
||||||
let exit_matcher = parser_with_context!(exit_matcher_parser)(&parser_context);
|
let exit_matcher = parser_with_context!(exit_matcher_parser)(&parser_context);
|
||||||
not(exit_matcher)(remaining)?;
|
not(exit_matcher)(remaining)?;
|
||||||
let (remaining, leading_blank_lines) = opt(consumed(tuple((
|
let contents_begin = remaining;
|
||||||
blank_line,
|
let blank_line_context = ContextElement::ExitMatcherNode(ExitMatcherNode {
|
||||||
many0(preceded(not(exit_matcher), blank_line)),
|
class: ExitClass::Alpha,
|
||||||
))))(remaining)?;
|
exit_matcher: &leading_blank_lines_end,
|
||||||
let leading_blank_lines =
|
|
||||||
leading_blank_lines.map(|(source, (first_line, _remaining_lines))| {
|
|
||||||
let mut element = Element::Paragraph(Paragraph::of_text(first_line.into()));
|
|
||||||
element.set_source(source.into());
|
|
||||||
element
|
|
||||||
});
|
});
|
||||||
|
let blank_line_context = parser_context.with_additional_node(&blank_line_context);
|
||||||
|
|
||||||
|
let (remaining, leading_blank_lines) =
|
||||||
|
opt(bind_context!(empty_paragraph, &blank_line_context))(remaining)?;
|
||||||
let (remaining, (mut children, _exit_contents)) =
|
let (remaining, (mut children, _exit_contents)) =
|
||||||
many_till(element_matcher, exit_matcher)(remaining)?;
|
many_till(element_matcher, exit_matcher)(remaining)?;
|
||||||
if let Some(lines) = leading_blank_lines {
|
if let Some(lines) = leading_blank_lines {
|
||||||
children.insert(0, lines);
|
children.insert(0, Element::Paragraph(lines));
|
||||||
}
|
}
|
||||||
|
let contents = get_consumed(contents_begin, remaining);
|
||||||
|
|
||||||
let (remaining, _end) = dynamic_block_end(&parser_context, remaining)?;
|
let (remaining, _end) = dynamic_block_end(&parser_context, remaining)?;
|
||||||
|
|
||||||
let (remaining, _trailing_ws) =
|
let (remaining, post_blank) =
|
||||||
maybe_consume_trailing_whitespace_if_not_exiting(context, remaining)?;
|
maybe_consume_trailing_whitespace_if_not_exiting(context, remaining)?;
|
||||||
let source = get_consumed(input, remaining);
|
let source = get_consumed(input, remaining);
|
||||||
Ok((
|
Ok((
|
||||||
@@ -114,6 +111,12 @@ where
|
|||||||
block_name: name.into(),
|
block_name: name.into(),
|
||||||
parameters: parameters.map(|val| val.into()),
|
parameters: parameters.map(|val| val.into()),
|
||||||
children,
|
children,
|
||||||
|
contents: if contents.len() > 0 {
|
||||||
|
Some(Into::<&str>::into(contents))
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
},
|
||||||
|
post_blank: post_blank.map(Into::<&str>::into),
|
||||||
},
|
},
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ pub(crate) fn entity<'b, 'g, 'r, 's>(
|
|||||||
let (remaining, _) = tag("\\")(input)?;
|
let (remaining, _) = tag("\\")(input)?;
|
||||||
let (remaining, (entity_definition, entity_name, use_brackets)) = name(context, remaining)?;
|
let (remaining, (entity_definition, entity_name, use_brackets)) = name(context, remaining)?;
|
||||||
|
|
||||||
let (remaining, _trailing_whitespace) =
|
let (remaining, post_blank) =
|
||||||
maybe_consume_object_trailing_whitespace_if_not_exiting(context, remaining)?;
|
maybe_consume_object_trailing_whitespace_if_not_exiting(context, remaining)?;
|
||||||
|
|
||||||
let source = get_consumed(input, remaining);
|
let source = get_consumed(input, remaining);
|
||||||
@@ -43,6 +43,7 @@ pub(crate) fn entity<'b, 'g, 'r, 's>(
|
|||||||
ascii: entity_definition.ascii,
|
ascii: entity_definition.ascii,
|
||||||
utf8: entity_definition.utf8,
|
utf8: entity_definition.utf8,
|
||||||
use_brackets,
|
use_brackets,
|
||||||
|
post_blank: post_blank.map(Into::<&str>::into),
|
||||||
},
|
},
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -39,7 +39,7 @@ pub(crate) fn export_snippet<'b, 'g, 'r, 's>(
|
|||||||
parser_with_context!(contents)(&parser_context),
|
parser_with_context!(contents)(&parser_context),
|
||||||
)))(remaining)?;
|
)))(remaining)?;
|
||||||
let (remaining, _) = tag("@@")(remaining)?;
|
let (remaining, _) = tag("@@")(remaining)?;
|
||||||
let (remaining, _trailing_whitespace) =
|
let (remaining, post_blank) =
|
||||||
maybe_consume_object_trailing_whitespace_if_not_exiting(context, remaining)?;
|
maybe_consume_object_trailing_whitespace_if_not_exiting(context, remaining)?;
|
||||||
let source = get_consumed(input, remaining);
|
let source = get_consumed(input, remaining);
|
||||||
Ok((
|
Ok((
|
||||||
@@ -48,6 +48,7 @@ pub(crate) fn export_snippet<'b, 'g, 'r, 's>(
|
|||||||
source: source.into(),
|
source: source.into(),
|
||||||
backend: backend_name.into(),
|
backend: backend_name.into(),
|
||||||
contents: backend_contents.map(|(_colon, backend_contents)| backend_contents.into()),
|
contents: backend_contents.map(|(_colon, backend_contents)| backend_contents.into()),
|
||||||
|
post_blank: post_blank.map(Into::<&str>::into),
|
||||||
},
|
},
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,12 +2,15 @@ use nom::branch::alt;
|
|||||||
use nom::bytes::complete::tag;
|
use nom::bytes::complete::tag;
|
||||||
use nom::character::complete::anychar;
|
use nom::character::complete::anychar;
|
||||||
use nom::character::complete::space0;
|
use nom::character::complete::space0;
|
||||||
|
use nom::combinator::map;
|
||||||
use nom::combinator::not;
|
use nom::combinator::not;
|
||||||
|
use nom::combinator::peek;
|
||||||
use nom::combinator::recognize;
|
use nom::combinator::recognize;
|
||||||
use nom::multi::many0;
|
use nom::multi::many0;
|
||||||
use nom::multi::many_till;
|
use nom::multi::many_till;
|
||||||
use nom::sequence::preceded;
|
use nom::sequence::preceded;
|
||||||
use nom::sequence::tuple;
|
use nom::sequence::tuple;
|
||||||
|
use nom::InputTake;
|
||||||
|
|
||||||
use super::affiliated_keyword::parse_affiliated_keywords;
|
use super::affiliated_keyword::parse_affiliated_keywords;
|
||||||
use super::org_source::OrgSource;
|
use super::org_source::OrgSource;
|
||||||
@@ -35,28 +38,25 @@ pub(crate) fn fixed_width_area<'b, 'g, 'r, 's, AK>(
|
|||||||
where
|
where
|
||||||
AK: IntoIterator<Item = Keyword<'s>>,
|
AK: IntoIterator<Item = Keyword<'s>>,
|
||||||
{
|
{
|
||||||
let fixed_width_area_line_matcher = parser_with_context!(fixed_width_area_line)(context);
|
|
||||||
let exit_matcher = parser_with_context!(exit_matcher_parser)(context);
|
let exit_matcher = parser_with_context!(exit_matcher_parser)(context);
|
||||||
let (remaining, first_line) = fixed_width_area_line_matcher(remaining)?;
|
let (remaining, first_line) = fixed_width_area_line(remaining)?;
|
||||||
let (remaining, mut remaining_lines) =
|
let (remaining, remaining_lines) = many0(preceded(
|
||||||
many0(preceded(not(exit_matcher), fixed_width_area_line_matcher))(remaining)?;
|
not(tuple((org_line_ending, exit_matcher))),
|
||||||
|
map(
|
||||||
|
tuple((org_line_ending, fixed_width_area_line)),
|
||||||
|
|(_line_ending, line_contents)| line_contents,
|
||||||
|
),
|
||||||
|
))(remaining)?;
|
||||||
|
|
||||||
let (remaining, _trailing_ws) =
|
let post_blank_begin = remaining;
|
||||||
|
let (remaining, _first_line_break) = org_line_ending(remaining)?;
|
||||||
|
let (remaining, _additional_post_blank) =
|
||||||
maybe_consume_trailing_whitespace_if_not_exiting(context, remaining)?;
|
maybe_consume_trailing_whitespace_if_not_exiting(context, remaining)?;
|
||||||
|
let post_blank = get_consumed(post_blank_begin, remaining);
|
||||||
let source = get_consumed(input, remaining);
|
let source = get_consumed(input, remaining);
|
||||||
let mut value = Vec::with_capacity(remaining_lines.len() + 1);
|
let mut value = Vec::with_capacity(remaining_lines.len() + 1);
|
||||||
let last_line = remaining_lines.pop();
|
|
||||||
if let Some(last_line) = last_line {
|
|
||||||
value.push(Into::<&str>::into(first_line));
|
value.push(Into::<&str>::into(first_line));
|
||||||
value.extend(remaining_lines.into_iter().map(Into::<&str>::into));
|
value.extend(remaining_lines.into_iter().map(Into::<&str>::into));
|
||||||
let last_line = Into::<&str>::into(last_line);
|
|
||||||
// Trim the line ending from the final line.
|
|
||||||
value.push(&last_line[..(last_line.len() - 1)])
|
|
||||||
} else {
|
|
||||||
// Trim the line ending from the only line.
|
|
||||||
let only_line = Into::<&str>::into(first_line);
|
|
||||||
value.push(&only_line[..(only_line.len() - 1)])
|
|
||||||
}
|
|
||||||
Ok((
|
Ok((
|
||||||
remaining,
|
remaining,
|
||||||
FixedWidthArea {
|
FixedWidthArea {
|
||||||
@@ -66,25 +66,24 @@ where
|
|||||||
affiliated_keywords,
|
affiliated_keywords,
|
||||||
),
|
),
|
||||||
value,
|
value,
|
||||||
|
post_blank: if post_blank.len() > 0 {
|
||||||
|
Some(Into::<&str>::into(post_blank))
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
},
|
||||||
},
|
},
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg_attr(
|
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
|
||||||
feature = "tracing",
|
fn fixed_width_area_line<'s>(input: OrgSource<'s>) -> Res<OrgSource<'s>, OrgSource<'s>> {
|
||||||
tracing::instrument(ret, level = "debug", skip(_context))
|
|
||||||
)]
|
|
||||||
fn fixed_width_area_line<'b, 'g, 'r, 's>(
|
|
||||||
_context: RefContext<'b, 'g, 'r, 's>,
|
|
||||||
input: OrgSource<'s>,
|
|
||||||
) -> Res<OrgSource<'s>, OrgSource<'s>> {
|
|
||||||
start_of_line(input)?;
|
start_of_line(input)?;
|
||||||
let (remaining, _) = tuple((space0, tag(":")))(input)?;
|
let (remaining, _) = tuple((space0, tag(":")))(input)?;
|
||||||
if let Ok((remaining, line_break)) = org_line_ending(remaining) {
|
if let Ok((_remain, _line_break)) = org_line_ending(remaining) {
|
||||||
return Ok((remaining, line_break));
|
return Ok((remaining, remaining.take(0)));
|
||||||
}
|
}
|
||||||
let (remaining, _) = tag(" ")(remaining)?;
|
let (remaining, _) = tag(" ")(remaining)?;
|
||||||
let (remaining, value) = recognize(many_till(anychar, org_line_ending))(remaining)?;
|
let (remaining, value) = recognize(many_till(anychar, peek(org_line_ending)))(remaining)?;
|
||||||
Ok((remaining, value))
|
Ok((remaining, value))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -75,6 +75,7 @@ where
|
|||||||
let parser_context = parser_context.with_additional_node(&contexts[2]);
|
let parser_context = parser_context.with_additional_node(&contexts[2]);
|
||||||
let element_matcher = parser_with_context!(element(true))(&parser_context);
|
let element_matcher = parser_with_context!(element(true))(&parser_context);
|
||||||
let exit_matcher = parser_with_context!(exit_matcher_parser)(&parser_context);
|
let exit_matcher = parser_with_context!(exit_matcher_parser)(&parser_context);
|
||||||
|
let before_contents = remaining;
|
||||||
let (mut remaining, (mut children, _exit_contents)) =
|
let (mut remaining, (mut children, _exit_contents)) =
|
||||||
many_till(include_input(element_matcher), exit_matcher)(remaining)?;
|
many_till(include_input(element_matcher), exit_matcher)(remaining)?;
|
||||||
|
|
||||||
@@ -90,13 +91,16 @@ where
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let (remaining, _trailing_ws) =
|
let contents = get_consumed(before_contents, remaining);
|
||||||
|
let (remaining, post_blank) =
|
||||||
maybe_consume_trailing_whitespace_if_not_exiting(context, remaining)?;
|
maybe_consume_trailing_whitespace_if_not_exiting(context, remaining)?;
|
||||||
let source = get_consumed(input, remaining);
|
let source = get_consumed(input, remaining);
|
||||||
Ok((
|
Ok((
|
||||||
remaining,
|
remaining,
|
||||||
FootnoteDefinition {
|
FootnoteDefinition {
|
||||||
source: source.into(),
|
source: source.into(),
|
||||||
|
contents: Some(contents.into()),
|
||||||
|
post_blank: post_blank.map(Into::<&str>::into),
|
||||||
affiliated_keywords: parse_affiliated_keywords(
|
affiliated_keywords: parse_affiliated_keywords(
|
||||||
context.get_global_settings(),
|
context.get_global_settings(),
|
||||||
affiliated_keywords,
|
affiliated_keywords,
|
||||||
@@ -160,7 +164,7 @@ mod tests {
|
|||||||
use crate::context::Context;
|
use crate::context::Context;
|
||||||
use crate::context::GlobalSettings;
|
use crate::context::GlobalSettings;
|
||||||
use crate::context::List;
|
use crate::context::List;
|
||||||
use crate::types::GetStandardProperties;
|
use crate::types::StandardProperties;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn two_paragraphs() {
|
fn two_paragraphs() {
|
||||||
@@ -181,17 +185,13 @@ line footnote.",
|
|||||||
footnote_definition_matcher(remaining).expect("Parse second footnote_definition.");
|
footnote_definition_matcher(remaining).expect("Parse second footnote_definition.");
|
||||||
assert_eq!(Into::<&str>::into(remaining), "");
|
assert_eq!(Into::<&str>::into(remaining), "");
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
first_footnote_definition
|
first_footnote_definition.get_source(),
|
||||||
.get_standard_properties()
|
|
||||||
.get_source(),
|
|
||||||
"[fn:1] A footnote.
|
"[fn:1] A footnote.
|
||||||
|
|
||||||
"
|
"
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
second_footnote_definition
|
second_footnote_definition.get_source(),
|
||||||
.get_standard_properties()
|
|
||||||
.get_source(),
|
|
||||||
"[fn:2] A multi-
|
"[fn:2] A multi-
|
||||||
|
|
||||||
line footnote."
|
line footnote."
|
||||||
@@ -216,9 +216,7 @@ not in the footnote.",
|
|||||||
footnote_definition_matcher(input).expect("Parse first footnote_definition");
|
footnote_definition_matcher(input).expect("Parse first footnote_definition");
|
||||||
assert_eq!(Into::<&str>::into(remaining), "not in the footnote.");
|
assert_eq!(Into::<&str>::into(remaining), "not in the footnote.");
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
first_footnote_definition
|
first_footnote_definition.get_source(),
|
||||||
.get_standard_properties()
|
|
||||||
.get_source(),
|
|
||||||
"[fn:2] A multi-
|
"[fn:2] A multi-
|
||||||
|
|
||||||
line footnote.
|
line footnote.
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ use nom::branch::alt;
|
|||||||
use nom::bytes::complete::tag;
|
use nom::bytes::complete::tag;
|
||||||
use nom::bytes::complete::tag_no_case;
|
use nom::bytes::complete::tag_no_case;
|
||||||
use nom::combinator::all_consuming;
|
use nom::combinator::all_consuming;
|
||||||
|
use nom::combinator::consumed;
|
||||||
use nom::combinator::map_parser;
|
use nom::combinator::map_parser;
|
||||||
use nom::combinator::verify;
|
use nom::combinator::verify;
|
||||||
use nom::multi::many1;
|
use nom::multi::many1;
|
||||||
@@ -59,7 +60,7 @@ fn anonymous_footnote<'b, 'g, 'r, 's>(
|
|||||||
let initial_context = ContextElement::document_context();
|
let initial_context = ContextElement::document_context();
|
||||||
let initial_context = Context::new(context.get_global_settings(), List::new(&initial_context));
|
let initial_context = Context::new(context.get_global_settings(), List::new(&initial_context));
|
||||||
|
|
||||||
let (remaining, children) = map_parser(
|
let (remaining, (contents, children)) = consumed(map_parser(
|
||||||
verify(
|
verify(
|
||||||
parser_with_context!(text_until_exit)(&parser_context),
|
parser_with_context!(text_until_exit)(&parser_context),
|
||||||
|text| text.len() > 0,
|
|text| text.len() > 0,
|
||||||
@@ -69,17 +70,19 @@ fn anonymous_footnote<'b, 'g, 'r, 's>(
|
|||||||
&initial_context,
|
&initial_context,
|
||||||
)))(i)
|
)))(i)
|
||||||
}),
|
}),
|
||||||
)(remaining)?;
|
))(remaining)?;
|
||||||
|
|
||||||
let (remaining, _) = tag("]")(remaining)?;
|
let (remaining, _) = tag("]")(remaining)?;
|
||||||
|
|
||||||
let (remaining, _trailing_whitespace) =
|
let (remaining, post_blank) =
|
||||||
maybe_consume_object_trailing_whitespace_if_not_exiting(context, remaining)?;
|
maybe_consume_object_trailing_whitespace_if_not_exiting(context, remaining)?;
|
||||||
let source = get_consumed(input, remaining);
|
let source = get_consumed(input, remaining);
|
||||||
Ok((
|
Ok((
|
||||||
remaining,
|
remaining,
|
||||||
FootnoteReference {
|
FootnoteReference {
|
||||||
source: source.into(),
|
source: source.into(),
|
||||||
|
contents: Some(contents.into()),
|
||||||
|
post_blank: post_blank.map(Into::<&str>::into),
|
||||||
label: None,
|
label: None,
|
||||||
definition: children,
|
definition: children,
|
||||||
},
|
},
|
||||||
@@ -106,7 +109,7 @@ fn inline_footnote<'b, 'g, 'r, 's>(
|
|||||||
let initial_context = ContextElement::document_context();
|
let initial_context = ContextElement::document_context();
|
||||||
let initial_context = Context::new(context.get_global_settings(), List::new(&initial_context));
|
let initial_context = Context::new(context.get_global_settings(), List::new(&initial_context));
|
||||||
|
|
||||||
let (remaining, children) = map_parser(
|
let (remaining, (contents, children)) = consumed(map_parser(
|
||||||
verify(
|
verify(
|
||||||
parser_with_context!(text_until_exit)(&parser_context),
|
parser_with_context!(text_until_exit)(&parser_context),
|
||||||
|text| text.len() > 0,
|
|text| text.len() > 0,
|
||||||
@@ -116,17 +119,19 @@ fn inline_footnote<'b, 'g, 'r, 's>(
|
|||||||
&initial_context,
|
&initial_context,
|
||||||
)))(i)
|
)))(i)
|
||||||
}),
|
}),
|
||||||
)(remaining)?;
|
))(remaining)?;
|
||||||
|
|
||||||
let (remaining, _) = tag("]")(remaining)?;
|
let (remaining, _) = tag("]")(remaining)?;
|
||||||
|
|
||||||
let (remaining, _trailing_whitespace) =
|
let (remaining, post_blank) =
|
||||||
maybe_consume_object_trailing_whitespace_if_not_exiting(context, remaining)?;
|
maybe_consume_object_trailing_whitespace_if_not_exiting(context, remaining)?;
|
||||||
let source = get_consumed(input, remaining);
|
let source = get_consumed(input, remaining);
|
||||||
Ok((
|
Ok((
|
||||||
remaining,
|
remaining,
|
||||||
FootnoteReference {
|
FootnoteReference {
|
||||||
source: source.into(),
|
source: source.into(),
|
||||||
|
contents: Some(contents.into()),
|
||||||
|
post_blank: post_blank.map(Into::<&str>::into),
|
||||||
label: Some(label_contents.into()),
|
label: Some(label_contents.into()),
|
||||||
definition: children,
|
definition: children,
|
||||||
},
|
},
|
||||||
@@ -144,13 +149,15 @@ fn footnote_reference_only<'b, 'g, 'r, 's>(
|
|||||||
let (remaining, _) = tag_no_case("[fn:")(input)?;
|
let (remaining, _) = tag_no_case("[fn:")(input)?;
|
||||||
let (remaining, label_contents) = label(remaining)?;
|
let (remaining, label_contents) = label(remaining)?;
|
||||||
let (remaining, _) = tag("]")(remaining)?;
|
let (remaining, _) = tag("]")(remaining)?;
|
||||||
let (remaining, _trailing_whitespace) =
|
let (remaining, post_blank) =
|
||||||
maybe_consume_object_trailing_whitespace_if_not_exiting(context, remaining)?;
|
maybe_consume_object_trailing_whitespace_if_not_exiting(context, remaining)?;
|
||||||
let source = get_consumed(input, remaining);
|
let source = get_consumed(input, remaining);
|
||||||
Ok((
|
Ok((
|
||||||
remaining,
|
remaining,
|
||||||
FootnoteReference {
|
FootnoteReference {
|
||||||
source: source.into(),
|
source: source.into(),
|
||||||
|
contents: None,
|
||||||
|
post_blank: post_blank.map(Into::<&str>::into),
|
||||||
label: Some(label_contents.into()),
|
label: Some(label_contents.into()),
|
||||||
definition: Vec::with_capacity(0),
|
definition: Vec::with_capacity(0),
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -5,22 +5,21 @@ use nom::character::complete::anychar;
|
|||||||
use nom::character::complete::line_ending;
|
use nom::character::complete::line_ending;
|
||||||
use nom::character::complete::space0;
|
use nom::character::complete::space0;
|
||||||
use nom::character::complete::space1;
|
use nom::character::complete::space1;
|
||||||
use nom::combinator::consumed;
|
|
||||||
use nom::combinator::eof;
|
use nom::combinator::eof;
|
||||||
use nom::combinator::not;
|
use nom::combinator::not;
|
||||||
use nom::combinator::opt;
|
use nom::combinator::opt;
|
||||||
use nom::combinator::peek;
|
use nom::combinator::peek;
|
||||||
use nom::combinator::recognize;
|
use nom::combinator::recognize;
|
||||||
use nom::combinator::verify;
|
use nom::combinator::verify;
|
||||||
use nom::multi::many0;
|
|
||||||
use nom::multi::many_till;
|
use nom::multi::many_till;
|
||||||
use nom::sequence::preceded;
|
|
||||||
use nom::sequence::tuple;
|
use nom::sequence::tuple;
|
||||||
|
|
||||||
use super::affiliated_keyword::parse_affiliated_keywords;
|
use super::affiliated_keyword::parse_affiliated_keywords;
|
||||||
use super::org_source::OrgSource;
|
use super::org_source::OrgSource;
|
||||||
|
use super::paragraph::empty_paragraph;
|
||||||
use super::util::in_section;
|
use super::util::in_section;
|
||||||
use super::util::maybe_consume_trailing_whitespace_if_not_exiting;
|
use super::util::maybe_consume_trailing_whitespace_if_not_exiting;
|
||||||
|
use crate::context::bind_context;
|
||||||
use crate::context::parser_with_context;
|
use crate::context::parser_with_context;
|
||||||
use crate::context::ContextElement;
|
use crate::context::ContextElement;
|
||||||
use crate::context::ContextMatcher;
|
use crate::context::ContextMatcher;
|
||||||
@@ -37,9 +36,7 @@ use crate::parser::util::start_of_line;
|
|||||||
use crate::types::CenterBlock;
|
use crate::types::CenterBlock;
|
||||||
use crate::types::Element;
|
use crate::types::Element;
|
||||||
use crate::types::Keyword;
|
use crate::types::Keyword;
|
||||||
use crate::types::Paragraph;
|
|
||||||
use crate::types::QuoteBlock;
|
use crate::types::QuoteBlock;
|
||||||
use crate::types::SetSource;
|
|
||||||
use crate::types::SpecialBlock;
|
use crate::types::SpecialBlock;
|
||||||
|
|
||||||
#[cfg_attr(
|
#[cfg_attr(
|
||||||
@@ -103,7 +100,7 @@ fn center_block<'b, 'g, 'r, 's, AK>(
|
|||||||
where
|
where
|
||||||
AK: IntoIterator<Item = Keyword<'s>>,
|
AK: IntoIterator<Item = Keyword<'s>>,
|
||||||
{
|
{
|
||||||
let (remaining, (source, children)) = greater_block_body(
|
let (remaining, body) = greater_block_body(
|
||||||
context,
|
context,
|
||||||
input,
|
input,
|
||||||
pre_affiliated_keywords_input,
|
pre_affiliated_keywords_input,
|
||||||
@@ -113,12 +110,14 @@ where
|
|||||||
Ok((
|
Ok((
|
||||||
remaining,
|
remaining,
|
||||||
Element::CenterBlock(CenterBlock {
|
Element::CenterBlock(CenterBlock {
|
||||||
source,
|
source: body.source,
|
||||||
affiliated_keywords: parse_affiliated_keywords(
|
affiliated_keywords: parse_affiliated_keywords(
|
||||||
context.get_global_settings(),
|
context.get_global_settings(),
|
||||||
affiliated_keywords,
|
affiliated_keywords,
|
||||||
),
|
),
|
||||||
children,
|
children: body.children,
|
||||||
|
contents: body.contents,
|
||||||
|
post_blank: body.post_blank,
|
||||||
}),
|
}),
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
@@ -136,7 +135,7 @@ fn quote_block<'b, 'g, 'r, 's, AK>(
|
|||||||
where
|
where
|
||||||
AK: IntoIterator<Item = Keyword<'s>>,
|
AK: IntoIterator<Item = Keyword<'s>>,
|
||||||
{
|
{
|
||||||
let (remaining, (source, children)) = greater_block_body(
|
let (remaining, body) = greater_block_body(
|
||||||
context,
|
context,
|
||||||
input,
|
input,
|
||||||
pre_affiliated_keywords_input,
|
pre_affiliated_keywords_input,
|
||||||
@@ -146,12 +145,14 @@ where
|
|||||||
Ok((
|
Ok((
|
||||||
remaining,
|
remaining,
|
||||||
Element::QuoteBlock(QuoteBlock {
|
Element::QuoteBlock(QuoteBlock {
|
||||||
source,
|
source: body.source,
|
||||||
affiliated_keywords: parse_affiliated_keywords(
|
affiliated_keywords: parse_affiliated_keywords(
|
||||||
context.get_global_settings(),
|
context.get_global_settings(),
|
||||||
affiliated_keywords,
|
affiliated_keywords,
|
||||||
),
|
),
|
||||||
children,
|
children: body.children,
|
||||||
|
contents: body.contents,
|
||||||
|
post_blank: body.post_blank,
|
||||||
}),
|
}),
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
@@ -197,7 +198,7 @@ where
|
|||||||
AK: IntoIterator<Item = Keyword<'s>>,
|
AK: IntoIterator<Item = Keyword<'s>>,
|
||||||
{
|
{
|
||||||
let (remaining, parameters) = opt(tuple((space1, parameters)))(input)?;
|
let (remaining, parameters) = opt(tuple((space1, parameters)))(input)?;
|
||||||
let (remaining, (source, children)) = greater_block_body(
|
let (remaining, body) = greater_block_body(
|
||||||
context,
|
context,
|
||||||
remaining,
|
remaining,
|
||||||
pre_affiliated_keywords_input,
|
pre_affiliated_keywords_input,
|
||||||
@@ -207,18 +208,28 @@ where
|
|||||||
Ok((
|
Ok((
|
||||||
remaining,
|
remaining,
|
||||||
Element::SpecialBlock(SpecialBlock {
|
Element::SpecialBlock(SpecialBlock {
|
||||||
source,
|
source: body.source,
|
||||||
affiliated_keywords: parse_affiliated_keywords(
|
affiliated_keywords: parse_affiliated_keywords(
|
||||||
context.get_global_settings(),
|
context.get_global_settings(),
|
||||||
affiliated_keywords,
|
affiliated_keywords,
|
||||||
),
|
),
|
||||||
children,
|
children: body.children,
|
||||||
block_type: name,
|
block_type: name,
|
||||||
parameters: parameters.map(|(_, parameters)| Into::<&str>::into(parameters)),
|
parameters: parameters.map(|(_, parameters)| Into::<&str>::into(parameters)),
|
||||||
|
contents: body.contents,
|
||||||
|
post_blank: body.post_blank,
|
||||||
}),
|
}),
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
struct GreaterBlockBody<'s> {
|
||||||
|
source: &'s str,
|
||||||
|
children: Vec<Element<'s>>,
|
||||||
|
contents: Option<&'s str>,
|
||||||
|
post_blank: Option<&'s str>,
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg_attr(
|
#[cfg_attr(
|
||||||
feature = "tracing",
|
feature = "tracing",
|
||||||
tracing::instrument(ret, level = "debug", skip(context))
|
tracing::instrument(ret, level = "debug", skip(context))
|
||||||
@@ -229,7 +240,7 @@ fn greater_block_body<'c, 'b, 'g, 'r, 's>(
|
|||||||
pre_affiliated_keywords_input: OrgSource<'s>,
|
pre_affiliated_keywords_input: OrgSource<'s>,
|
||||||
name: &'c str,
|
name: &'c str,
|
||||||
context_name: &'c str,
|
context_name: &'c str,
|
||||||
) -> Res<OrgSource<'s>, (&'s str, Vec<Element<'s>>)> {
|
) -> Res<OrgSource<'s>, GreaterBlockBody<'s>> {
|
||||||
if in_section(context, context_name) {
|
if in_section(context, context_name) {
|
||||||
return Err(nom::Err::Error(CustomError::Static(
|
return Err(nom::Err::Error(CustomError::Static(
|
||||||
"Cannot nest objects of the same element",
|
"Cannot nest objects of the same element",
|
||||||
@@ -251,30 +262,43 @@ fn greater_block_body<'c, 'b, 'g, 'r, 's>(
|
|||||||
let element_matcher = parser_with_context!(element(true))(&parser_context);
|
let element_matcher = parser_with_context!(element(true))(&parser_context);
|
||||||
let exit_matcher = parser_with_context!(exit_matcher_parser)(&parser_context);
|
let exit_matcher = parser_with_context!(exit_matcher_parser)(&parser_context);
|
||||||
not(exit_matcher)(remaining)?;
|
not(exit_matcher)(remaining)?;
|
||||||
let (remaining, leading_blank_lines) = opt(consumed(tuple((
|
let contents_begin = remaining;
|
||||||
blank_line,
|
|
||||||
many0(preceded(not(exit_matcher), blank_line)),
|
let blank_line_context = ContextElement::ExitMatcherNode(ExitMatcherNode {
|
||||||
))))(remaining)?;
|
class: ExitClass::Alpha,
|
||||||
let leading_blank_lines =
|
exit_matcher: &leading_blank_lines_end,
|
||||||
leading_blank_lines.map(|(source, (first_line, _remaining_lines))| {
|
|
||||||
let mut element = Element::Paragraph(Paragraph::of_text(first_line.into()));
|
|
||||||
element.set_source(source.into());
|
|
||||||
element
|
|
||||||
});
|
});
|
||||||
|
let blank_line_context = parser_context.with_additional_node(&blank_line_context);
|
||||||
|
|
||||||
|
let (remaining, leading_blank_lines) =
|
||||||
|
opt(bind_context!(empty_paragraph, &blank_line_context))(remaining)?;
|
||||||
let (remaining, (mut children, _exit_contents)) =
|
let (remaining, (mut children, _exit_contents)) =
|
||||||
many_till(element_matcher, exit_matcher)(remaining)?;
|
many_till(element_matcher, exit_matcher)(remaining)?;
|
||||||
if let Some(lines) = leading_blank_lines {
|
if let Some(lines) = leading_blank_lines {
|
||||||
children.insert(0, lines);
|
children.insert(0, Element::Paragraph(lines));
|
||||||
}
|
}
|
||||||
|
let contents = get_consumed(contents_begin, remaining);
|
||||||
|
|
||||||
let (remaining, _end) = exit_with_name(&parser_context, remaining)?;
|
let (remaining, _end) = exit_with_name(&parser_context, remaining)?;
|
||||||
|
|
||||||
// Not checking if parent exit matcher is causing exit because the greater_block_end matcher asserts we matched a full greater block
|
// Not checking if parent exit matcher is causing exit because the greater_block_end matcher asserts we matched a full greater block
|
||||||
|
|
||||||
let (remaining, _trailing_ws) =
|
let (remaining, post_blank) =
|
||||||
maybe_consume_trailing_whitespace_if_not_exiting(context, remaining)?;
|
maybe_consume_trailing_whitespace_if_not_exiting(context, remaining)?;
|
||||||
let source = get_consumed(pre_affiliated_keywords_input, remaining);
|
let source = get_consumed(pre_affiliated_keywords_input, remaining);
|
||||||
Ok((remaining, (Into::<&str>::into(source), children)))
|
Ok((
|
||||||
|
remaining,
|
||||||
|
GreaterBlockBody {
|
||||||
|
source: Into::<&str>::into(source),
|
||||||
|
children,
|
||||||
|
contents: if contents.len() > 0 {
|
||||||
|
Some(Into::<&str>::into(contents))
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
},
|
||||||
|
post_blank: post_blank.map(Into::<&str>::into),
|
||||||
|
},
|
||||||
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
|
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
|
||||||
@@ -310,3 +334,14 @@ fn _greater_block_end<'b, 'g, 'r, 's, 'c>(
|
|||||||
let source = get_consumed(input, remaining);
|
let source = get_consumed(input, remaining);
|
||||||
Ok((remaining, source))
|
Ok((remaining, source))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg_attr(
|
||||||
|
feature = "tracing",
|
||||||
|
tracing::instrument(ret, level = "debug", skip(_context))
|
||||||
|
)]
|
||||||
|
pub(crate) fn leading_blank_lines_end<'b, 'g, 'r, 's, 'c>(
|
||||||
|
_context: RefContext<'b, 'g, 'r, 's>,
|
||||||
|
input: OrgSource<'s>,
|
||||||
|
) -> Res<OrgSource<'s>, OrgSource<'s>> {
|
||||||
|
recognize(not(blank_line))(input)
|
||||||
|
}
|
||||||
|
|||||||
@@ -66,6 +66,8 @@ fn _heading<'b, 'g, 'r, 's>(
|
|||||||
let (remaining, pre_headline) = headline(context, input, parent_star_count)?;
|
let (remaining, pre_headline) = headline(context, input, parent_star_count)?;
|
||||||
let section_matcher = bind_context!(section, context);
|
let section_matcher = bind_context!(section, context);
|
||||||
let heading_matcher = bind_context!(heading(pre_headline.star_count), context);
|
let heading_matcher = bind_context!(heading(pre_headline.star_count), context);
|
||||||
|
let (contents_begin, _) = opt(many0(blank_line))(remaining)?;
|
||||||
|
let maybe_post_blank = get_consumed(remaining, contents_begin);
|
||||||
let (remaining, maybe_section) =
|
let (remaining, maybe_section) =
|
||||||
opt(map(section_matcher, DocumentElement::Section))(remaining)?;
|
opt(map(section_matcher, DocumentElement::Section))(remaining)?;
|
||||||
let (remaining, _ws) = opt(tuple((start_of_line, many0(blank_line))))(remaining)?;
|
let (remaining, _ws) = opt(tuple((start_of_line, many0(blank_line))))(remaining)?;
|
||||||
@@ -82,7 +84,8 @@ fn _heading<'b, 'g, 'r, 's>(
|
|||||||
}
|
}
|
||||||
children.insert(0, section);
|
children.insert(0, section);
|
||||||
}
|
}
|
||||||
let remaining = if children.is_empty() {
|
let has_children = !children.is_empty();
|
||||||
|
let remaining = if !has_children {
|
||||||
// Support empty headings
|
// Support empty headings
|
||||||
let (remain, _ws) = many0(blank_line)(remaining)?;
|
let (remain, _ws) = many0(blank_line)(remaining)?;
|
||||||
remain
|
remain
|
||||||
@@ -91,6 +94,7 @@ fn _heading<'b, 'g, 'r, 's>(
|
|||||||
};
|
};
|
||||||
let is_archived = pre_headline.tags.contains(&"ARCHIVE");
|
let is_archived = pre_headline.tags.contains(&"ARCHIVE");
|
||||||
|
|
||||||
|
let contents = get_consumed(contents_begin, remaining);
|
||||||
let source = get_consumed(input, remaining);
|
let source = get_consumed(input, remaining);
|
||||||
Ok((
|
Ok((
|
||||||
remaining,
|
remaining,
|
||||||
@@ -112,6 +116,16 @@ fn _heading<'b, 'g, 'r, 's>(
|
|||||||
scheduled,
|
scheduled,
|
||||||
deadline,
|
deadline,
|
||||||
closed,
|
closed,
|
||||||
|
contents: if contents.len() > 0 {
|
||||||
|
Some(Into::<&str>::into(contents))
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
},
|
||||||
|
post_blank: if has_children {
|
||||||
|
None
|
||||||
|
} else {
|
||||||
|
Some(Into::<&str>::into(maybe_post_blank))
|
||||||
|
},
|
||||||
},
|
},
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -38,7 +38,7 @@ where
|
|||||||
space0,
|
space0,
|
||||||
alt((line_ending, eof)),
|
alt((line_ending, eof)),
|
||||||
)))(remaining)?;
|
)))(remaining)?;
|
||||||
let (remaining, _trailing_ws) =
|
let (remaining, post_blank) =
|
||||||
maybe_consume_trailing_whitespace_if_not_exiting(context, remaining)?;
|
maybe_consume_trailing_whitespace_if_not_exiting(context, remaining)?;
|
||||||
let source = get_consumed(input, remaining);
|
let source = get_consumed(input, remaining);
|
||||||
Ok((
|
Ok((
|
||||||
@@ -49,6 +49,7 @@ where
|
|||||||
context.get_global_settings(),
|
context.get_global_settings(),
|
||||||
affiliated_keywords,
|
affiliated_keywords,
|
||||||
),
|
),
|
||||||
|
post_blank: post_blank.map(Into::<&str>::into),
|
||||||
},
|
},
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -102,7 +102,7 @@ pub(crate) fn apply_in_buffer_settings<'g, 's, 'sf>(
|
|||||||
}) {
|
}) {
|
||||||
let (_, (in_progress_words, complete_words)) =
|
let (_, (in_progress_words, complete_words)) =
|
||||||
todo_keywords(kw.value).map_err(|err| match err {
|
todo_keywords(kw.value).map_err(|err| match err {
|
||||||
nom::Err::Incomplete(_) => CustomError::Text(err.to_string()),
|
nom::Err::Incomplete(_) => panic!("This parser does not support streaming."),
|
||||||
nom::Err::Error(e) => e,
|
nom::Err::Error(e) => e,
|
||||||
nom::Err::Failure(e) => e,
|
nom::Err::Failure(e) => e,
|
||||||
})?;
|
})?;
|
||||||
@@ -123,7 +123,7 @@ pub(crate) fn apply_in_buffer_settings<'g, 's, 'sf>(
|
|||||||
kw.value,
|
kw.value,
|
||||||
)
|
)
|
||||||
.map_err(|err: nom::Err<_>| match err {
|
.map_err(|err: nom::Err<_>| match err {
|
||||||
nom::Err::Incomplete(_) => CustomError::Text(err.to_string()),
|
nom::Err::Incomplete(_) => panic!("This parser does not support streaming."),
|
||||||
nom::Err::Error(e) => e,
|
nom::Err::Error(e) => e,
|
||||||
nom::Err::Failure(e) => e,
|
nom::Err::Failure(e) => e,
|
||||||
})?;
|
})?;
|
||||||
@@ -141,7 +141,7 @@ pub(crate) fn apply_in_buffer_settings<'g, 's, 'sf>(
|
|||||||
.filter(|kw| kw.key.eq_ignore_ascii_case("link"))
|
.filter(|kw| kw.key.eq_ignore_ascii_case("link"))
|
||||||
{
|
{
|
||||||
let (_, (link_key, link_value)) = link_template(kw.value).map_err(|err| match err {
|
let (_, (link_key, link_value)) = link_template(kw.value).map_err(|err| match err {
|
||||||
nom::Err::Incomplete(_) => CustomError::Text(err.to_string()),
|
nom::Err::Incomplete(_) => panic!("This parser does not support streaming."),
|
||||||
nom::Err::Error(e) => e,
|
nom::Err::Error(e) => e,
|
||||||
nom::Err::Failure(e) => e,
|
nom::Err::Failure(e) => e,
|
||||||
})?;
|
})?;
|
||||||
@@ -157,7 +157,7 @@ pub(crate) fn apply_in_buffer_settings<'g, 's, 'sf>(
|
|||||||
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
|
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
|
||||||
pub(crate) fn apply_post_parse_in_buffer_settings<'g, 's, 'sf>(document: &mut Document<'s>) {
|
pub(crate) fn apply_post_parse_in_buffer_settings<'g, 's, 'sf>(document: &mut Document<'s>) {
|
||||||
document.category = Into::<AstNode>::into(&*document)
|
document.category = Into::<AstNode>::into(&*document)
|
||||||
.into_iter()
|
.iter_all_ast_nodes()
|
||||||
.filter_map(|ast_node| {
|
.filter_map(|ast_node| {
|
||||||
if let AstNode::Keyword(ast_node) = ast_node {
|
if let AstNode::Keyword(ast_node) = ast_node {
|
||||||
if ast_node.key.eq_ignore_ascii_case("category") {
|
if ast_node.key.eq_ignore_ascii_case("category") {
|
||||||
|
|||||||
@@ -38,7 +38,7 @@ pub(crate) fn inline_babel_call<'b, 'g, 'r, 's>(
|
|||||||
let (remaining, arguments) = argument(context, remaining)?;
|
let (remaining, arguments) = argument(context, remaining)?;
|
||||||
let (remaining, end_header) = opt(parser_with_context!(header)(context))(remaining)?;
|
let (remaining, end_header) = opt(parser_with_context!(header)(context))(remaining)?;
|
||||||
let value = get_consumed(input, remaining);
|
let value = get_consumed(input, remaining);
|
||||||
let (remaining, _trailing_whitespace) =
|
let (remaining, post_blank) =
|
||||||
maybe_consume_object_trailing_whitespace_if_not_exiting(context, remaining)?;
|
maybe_consume_object_trailing_whitespace_if_not_exiting(context, remaining)?;
|
||||||
let source = get_consumed(input, remaining);
|
let source = get_consumed(input, remaining);
|
||||||
Ok((
|
Ok((
|
||||||
@@ -54,6 +54,7 @@ pub(crate) fn inline_babel_call<'b, 'g, 'r, 's>(
|
|||||||
None
|
None
|
||||||
},
|
},
|
||||||
end_header: end_header.map(Into::<&str>::into),
|
end_header: end_header.map(Into::<&str>::into),
|
||||||
|
post_blank: post_blank.map(Into::<&str>::into),
|
||||||
},
|
},
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -38,7 +38,7 @@ pub(crate) fn inline_source_block<'b, 'g, 'r, 's>(
|
|||||||
let (remaining, language) = lang(context, remaining)?;
|
let (remaining, language) = lang(context, remaining)?;
|
||||||
let (remaining, parameters) = opt(parser_with_context!(header)(context))(remaining)?;
|
let (remaining, parameters) = opt(parser_with_context!(header)(context))(remaining)?;
|
||||||
let (remaining, value) = body(context, remaining)?;
|
let (remaining, value) = body(context, remaining)?;
|
||||||
let (remaining, _trailing_whitespace) =
|
let (remaining, post_blank) =
|
||||||
maybe_consume_object_trailing_whitespace_if_not_exiting(context, remaining)?;
|
maybe_consume_object_trailing_whitespace_if_not_exiting(context, remaining)?;
|
||||||
let source = get_consumed(input, remaining);
|
let source = get_consumed(input, remaining);
|
||||||
Ok((
|
Ok((
|
||||||
@@ -48,6 +48,7 @@ pub(crate) fn inline_source_block<'b, 'g, 'r, 's>(
|
|||||||
language: language.into(),
|
language: language.into(),
|
||||||
parameters: parameters.map(Into::<&str>::into),
|
parameters: parameters.map(Into::<&str>::into),
|
||||||
value: value.into(),
|
value: value.into(),
|
||||||
|
post_blank: post_blank.map(Into::<&str>::into),
|
||||||
},
|
},
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -58,6 +58,7 @@ fn _filtered_keyword<'s, F: Fn(OrgSource<'s>) -> Res<OrgSource<'s>, OrgSource<'s
|
|||||||
affiliated_keywords: AffiliatedKeywords::default(), // To be populated by the caller if this keyword is in a context to support affiliated keywords.
|
affiliated_keywords: AffiliatedKeywords::default(), // To be populated by the caller if this keyword is in a context to support affiliated keywords.
|
||||||
key: parsed_key.into(),
|
key: parsed_key.into(),
|
||||||
value: "",
|
value: "",
|
||||||
|
post_blank: None,
|
||||||
},
|
},
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
@@ -71,6 +72,7 @@ fn _filtered_keyword<'s, F: Fn(OrgSource<'s>) -> Res<OrgSource<'s>, OrgSource<'s
|
|||||||
affiliated_keywords: AffiliatedKeywords::default(), // To be populated by the caller if this keyword is in a context to support affiliated keywords.
|
affiliated_keywords: AffiliatedKeywords::default(), // To be populated by the caller if this keyword is in a context to support affiliated keywords.
|
||||||
key: parsed_key.into(),
|
key: parsed_key.into(),
|
||||||
value: parsed_value.into(),
|
value: parsed_value.into(),
|
||||||
|
post_blank: None,
|
||||||
},
|
},
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
@@ -89,12 +91,13 @@ where
|
|||||||
AK: IntoIterator<Item = Keyword<'s>>,
|
AK: IntoIterator<Item = Keyword<'s>>,
|
||||||
{
|
{
|
||||||
let (remaining, mut kw) = filtered_keyword(regular_keyword_key)(remaining)?;
|
let (remaining, mut kw) = filtered_keyword(regular_keyword_key)(remaining)?;
|
||||||
let (remaining, _trailing_ws) =
|
let (remaining, post_blank) =
|
||||||
maybe_consume_trailing_whitespace_if_not_exiting(context, remaining)?;
|
maybe_consume_trailing_whitespace_if_not_exiting(context, remaining)?;
|
||||||
let source = get_consumed(input, remaining);
|
let source = get_consumed(input, remaining);
|
||||||
kw.affiliated_keywords =
|
kw.affiliated_keywords =
|
||||||
parse_affiliated_keywords(context.get_global_settings(), affiliated_keywords);
|
parse_affiliated_keywords(context.get_global_settings(), affiliated_keywords);
|
||||||
kw.source = Into::<&str>::into(source);
|
kw.source = Into::<&str>::into(source);
|
||||||
|
kw.post_blank = post_blank.map(Into::<&str>::into);
|
||||||
Ok((remaining, kw))
|
Ok((remaining, kw))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -57,7 +57,7 @@ where
|
|||||||
let (remaining, _end) = latex_environment_end_specialized(&parser_context, remaining)?;
|
let (remaining, _end) = latex_environment_end_specialized(&parser_context, remaining)?;
|
||||||
let value_end = remaining;
|
let value_end = remaining;
|
||||||
|
|
||||||
let (remaining, _trailing_ws) =
|
let (remaining, post_blank) =
|
||||||
maybe_consume_trailing_whitespace_if_not_exiting(context, remaining)?;
|
maybe_consume_trailing_whitespace_if_not_exiting(context, remaining)?;
|
||||||
let source = get_consumed(input, remaining);
|
let source = get_consumed(input, remaining);
|
||||||
let value = get_consumed(value_start, value_end);
|
let value = get_consumed(value_start, value_end);
|
||||||
@@ -70,6 +70,7 @@ where
|
|||||||
affiliated_keywords,
|
affiliated_keywords,
|
||||||
),
|
),
|
||||||
value: value.into(),
|
value: value.into(),
|
||||||
|
post_blank: post_blank.map(Into::<&str>::into),
|
||||||
},
|
},
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -39,7 +39,7 @@ pub(crate) fn latex_fragment<'b, 'g, 'r, 's>(
|
|||||||
parser_with_context!(bordered_dollar_fragment)(context),
|
parser_with_context!(bordered_dollar_fragment)(context),
|
||||||
))(input)?;
|
))(input)?;
|
||||||
let value = get_consumed(input, remaining);
|
let value = get_consumed(input, remaining);
|
||||||
let (remaining, _trailing_whitespace) =
|
let (remaining, post_blank) =
|
||||||
maybe_consume_object_trailing_whitespace_if_not_exiting(context, remaining)?;
|
maybe_consume_object_trailing_whitespace_if_not_exiting(context, remaining)?;
|
||||||
let source = get_consumed(input, remaining);
|
let source = get_consumed(input, remaining);
|
||||||
Ok((
|
Ok((
|
||||||
@@ -47,6 +47,7 @@ pub(crate) fn latex_fragment<'b, 'g, 'r, 's>(
|
|||||||
LatexFragment {
|
LatexFragment {
|
||||||
source: source.into(),
|
source: source.into(),
|
||||||
value: value.into(),
|
value: value.into(),
|
||||||
|
post_blank: post_blank.map(Into::<&str>::into),
|
||||||
},
|
},
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -80,22 +80,28 @@ where
|
|||||||
let object_matcher = parser_with_context!(standard_set_object)(&parser_context);
|
let object_matcher = parser_with_context!(standard_set_object)(&parser_context);
|
||||||
let exit_matcher = parser_with_context!(exit_matcher_parser)(&parser_context);
|
let exit_matcher = parser_with_context!(exit_matcher_parser)(&parser_context);
|
||||||
// Check for a completely empty block
|
// Check for a completely empty block
|
||||||
let (remaining, children) = match consumed(many_till(blank_line, exit_matcher))(remaining) {
|
let (remaining, contents, children) =
|
||||||
|
match consumed(many_till(blank_line, exit_matcher))(remaining) {
|
||||||
Ok((remaining, (whitespace, (_children, _exit_contents)))) => (
|
Ok((remaining, (whitespace, (_children, _exit_contents)))) => (
|
||||||
remaining,
|
remaining,
|
||||||
|
whitespace,
|
||||||
|
if whitespace.len() > 0 {
|
||||||
vec![Object::PlainText(PlainText {
|
vec![Object::PlainText(PlainText {
|
||||||
source: whitespace.into(),
|
source: whitespace.into(),
|
||||||
})],
|
})]
|
||||||
|
} else {
|
||||||
|
Vec::new()
|
||||||
|
},
|
||||||
),
|
),
|
||||||
Err(_) => {
|
Err(_) => {
|
||||||
let (remaining, (children, _exit_contents)) =
|
let (remaining, (contents, (children, _exit_contents))) =
|
||||||
many_till(object_matcher, exit_matcher)(remaining)?;
|
consumed(many_till(object_matcher, exit_matcher))(remaining)?;
|
||||||
(remaining, children)
|
(remaining, contents, children)
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
let (remaining, _end) = lesser_block_end_specialized(&parser_context, remaining)?;
|
let (remaining, _end) = lesser_block_end_specialized(&parser_context, remaining)?;
|
||||||
|
|
||||||
let (remaining, _trailing_ws) =
|
let (remaining, post_blank) =
|
||||||
maybe_consume_trailing_whitespace_if_not_exiting(context, remaining)?;
|
maybe_consume_trailing_whitespace_if_not_exiting(context, remaining)?;
|
||||||
let source = get_consumed(input, remaining);
|
let source = get_consumed(input, remaining);
|
||||||
Ok((
|
Ok((
|
||||||
@@ -108,6 +114,8 @@ where
|
|||||||
),
|
),
|
||||||
data: parameters.map(Into::<&str>::into),
|
data: parameters.map(Into::<&str>::into),
|
||||||
children,
|
children,
|
||||||
|
contents: Into::<&str>::into(contents),
|
||||||
|
post_blank: post_blank.map(Into::<&str>::into),
|
||||||
},
|
},
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
@@ -144,7 +152,7 @@ where
|
|||||||
let (remaining, contents) = parser_with_context!(text_until_exit)(&parser_context)(remaining)?;
|
let (remaining, contents) = parser_with_context!(text_until_exit)(&parser_context)(remaining)?;
|
||||||
let (remaining, _end) = lesser_block_end_specialized(&parser_context, remaining)?;
|
let (remaining, _end) = lesser_block_end_specialized(&parser_context, remaining)?;
|
||||||
|
|
||||||
let (remaining, _trailing_ws) =
|
let (remaining, post_blank) =
|
||||||
maybe_consume_trailing_whitespace_if_not_exiting(context, remaining)?;
|
maybe_consume_trailing_whitespace_if_not_exiting(context, remaining)?;
|
||||||
let source = get_consumed(input, remaining);
|
let source = get_consumed(input, remaining);
|
||||||
Ok((
|
Ok((
|
||||||
@@ -156,6 +164,7 @@ where
|
|||||||
affiliated_keywords,
|
affiliated_keywords,
|
||||||
),
|
),
|
||||||
contents: contents.into(),
|
contents: contents.into(),
|
||||||
|
post_blank: post_blank.map(Into::<&str>::into),
|
||||||
},
|
},
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
@@ -202,10 +211,10 @@ where
|
|||||||
let parser_context = parser_context.with_additional_node(&contexts[1]);
|
let parser_context = parser_context.with_additional_node(&contexts[1]);
|
||||||
let parser_context = parser_context.with_additional_node(&contexts[2]);
|
let parser_context = parser_context.with_additional_node(&contexts[2]);
|
||||||
|
|
||||||
let (remaining, contents) = content(&parser_context, remaining)?;
|
let (remaining, contents) = text_until_exit(&parser_context, remaining)?;
|
||||||
let (remaining, _end) = lesser_block_end_specialized(&parser_context, remaining)?;
|
let (remaining, _end) = lesser_block_end_specialized(&parser_context, remaining)?;
|
||||||
|
|
||||||
let (remaining, _trailing_ws) =
|
let (remaining, post_blank) =
|
||||||
maybe_consume_trailing_whitespace_if_not_exiting(context, remaining)?;
|
maybe_consume_trailing_whitespace_if_not_exiting(context, remaining)?;
|
||||||
let source = get_consumed(input, remaining);
|
let source = get_consumed(input, remaining);
|
||||||
let (switches, number_lines, preserve_indent, retain_labels, use_labels, label_format) = {
|
let (switches, number_lines, preserve_indent, retain_labels, use_labels, label_format) = {
|
||||||
@@ -236,7 +245,8 @@ where
|
|||||||
retain_labels,
|
retain_labels,
|
||||||
use_labels,
|
use_labels,
|
||||||
label_format,
|
label_format,
|
||||||
contents,
|
value: Into::<&str>::into(contents),
|
||||||
|
post_blank: post_blank.map(Into::<&str>::into),
|
||||||
},
|
},
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
@@ -276,10 +286,10 @@ where
|
|||||||
let parser_context = parser_context.with_additional_node(&contexts[1]);
|
let parser_context = parser_context.with_additional_node(&contexts[1]);
|
||||||
let parser_context = parser_context.with_additional_node(&contexts[2]);
|
let parser_context = parser_context.with_additional_node(&contexts[2]);
|
||||||
|
|
||||||
let (remaining, contents) = content(&parser_context, remaining)?;
|
let (remaining, contents) = text_until_exit(&parser_context, remaining)?;
|
||||||
let (remaining, _end) = lesser_block_end_specialized(&parser_context, remaining)?;
|
let (remaining, _end) = lesser_block_end_specialized(&parser_context, remaining)?;
|
||||||
|
|
||||||
let (remaining, _trailing_ws) =
|
let (remaining, post_blank) =
|
||||||
maybe_consume_trailing_whitespace_if_not_exiting(context, remaining)?;
|
maybe_consume_trailing_whitespace_if_not_exiting(context, remaining)?;
|
||||||
let source = get_consumed(input, remaining);
|
let source = get_consumed(input, remaining);
|
||||||
Ok((
|
Ok((
|
||||||
@@ -292,7 +302,8 @@ where
|
|||||||
),
|
),
|
||||||
export_type: export_type.map(Into::<&str>::into),
|
export_type: export_type.map(Into::<&str>::into),
|
||||||
data: parameters.map(Into::<&str>::into),
|
data: parameters.map(Into::<&str>::into),
|
||||||
contents,
|
value: Into::<&str>::into(contents),
|
||||||
|
post_blank: post_blank.map(Into::<&str>::into),
|
||||||
},
|
},
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
@@ -331,10 +342,10 @@ where
|
|||||||
let parser_context = context.with_additional_node(&contexts[0]);
|
let parser_context = context.with_additional_node(&contexts[0]);
|
||||||
let parser_context = parser_context.with_additional_node(&contexts[1]);
|
let parser_context = parser_context.with_additional_node(&contexts[1]);
|
||||||
let parser_context = parser_context.with_additional_node(&contexts[2]);
|
let parser_context = parser_context.with_additional_node(&contexts[2]);
|
||||||
let (remaining, contents) = content(&parser_context, remaining)?;
|
let (remaining, contents) = text_until_exit(&parser_context, remaining)?;
|
||||||
let (remaining, _end) = lesser_block_end_specialized(&parser_context, remaining)?;
|
let (remaining, _end) = lesser_block_end_specialized(&parser_context, remaining)?;
|
||||||
|
|
||||||
let (remaining, _trailing_ws) =
|
let (remaining, post_blank) =
|
||||||
maybe_consume_trailing_whitespace_if_not_exiting(context, remaining)?;
|
maybe_consume_trailing_whitespace_if_not_exiting(context, remaining)?;
|
||||||
let source = get_consumed(input, remaining);
|
let source = get_consumed(input, remaining);
|
||||||
let (switches, number_lines, preserve_indent, retain_labels, use_labels, label_format) = {
|
let (switches, number_lines, preserve_indent, retain_labels, use_labels, label_format) = {
|
||||||
@@ -371,7 +382,8 @@ where
|
|||||||
retain_labels,
|
retain_labels,
|
||||||
use_labels,
|
use_labels,
|
||||||
label_format,
|
label_format,
|
||||||
contents,
|
value: Into::<&str>::into(contents),
|
||||||
|
post_blank: post_blank.map(Into::<&str>::into),
|
||||||
},
|
},
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
@@ -650,51 +662,3 @@ fn switch_word<'s>(input: OrgSource<'s>) -> Res<OrgSource<'s>, OrgSource<'s>> {
|
|||||||
is_not(" \t\r\n"),
|
is_not(" \t\r\n"),
|
||||||
))(input)
|
))(input)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg_attr(
|
|
||||||
feature = "tracing",
|
|
||||||
tracing::instrument(ret, level = "debug", skip(context))
|
|
||||||
)]
|
|
||||||
pub(crate) fn content<'b, 'g, 'r, 's>(
|
|
||||||
context: RefContext<'b, 'g, 'r, 's>,
|
|
||||||
input: OrgSource<'s>,
|
|
||||||
) -> Res<OrgSource<'s>, String> {
|
|
||||||
let mut ret = String::new();
|
|
||||||
let mut remaining = input;
|
|
||||||
let exit_matcher_parser = parser_with_context!(exit_matcher_parser)(context);
|
|
||||||
loop {
|
|
||||||
if exit_matcher_parser(remaining).is_ok() {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
let (remain, (pre_escape_whitespace, line)) = content_line(remaining)?;
|
|
||||||
if let Some(val) = pre_escape_whitespace {
|
|
||||||
ret.push_str(Into::<&str>::into(val));
|
|
||||||
}
|
|
||||||
ret.push_str(line.into());
|
|
||||||
remaining = remain;
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok((remaining, ret))
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
|
|
||||||
fn content_line<'s>(
|
|
||||||
input: OrgSource<'s>,
|
|
||||||
) -> Res<OrgSource<'s>, (Option<OrgSource<'s>>, OrgSource<'s>)> {
|
|
||||||
let (remaining, pre_escape_whitespace) = opt(map(
|
|
||||||
tuple((
|
|
||||||
recognize(tuple((
|
|
||||||
space0,
|
|
||||||
many_till(
|
|
||||||
tag(","),
|
|
||||||
peek(tuple((tag(","), alt((tag("#+"), tag("*")))))),
|
|
||||||
),
|
|
||||||
))),
|
|
||||||
tag(","),
|
|
||||||
)),
|
|
||||||
|(pre_comma, _)| pre_comma,
|
|
||||||
))(input)?;
|
|
||||||
let (remaining, line_post_escape) = recognize(many_till(anychar, line_ending))(remaining)?;
|
|
||||||
Ok((remaining, (pre_escape_whitespace, line_post_escape)))
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -32,7 +32,7 @@ pub(crate) fn org_macro<'b, 'g, 'r, 's>(
|
|||||||
let (remaining, macro_args) = opt(parser_with_context!(org_macro_args)(context))(remaining)?;
|
let (remaining, macro_args) = opt(parser_with_context!(org_macro_args)(context))(remaining)?;
|
||||||
let (remaining, _) = tag("}}}")(remaining)?;
|
let (remaining, _) = tag("}}}")(remaining)?;
|
||||||
let macro_value = get_consumed(input, remaining);
|
let macro_value = get_consumed(input, remaining);
|
||||||
let (remaining, _trailing_whitespace) =
|
let (remaining, post_blank) =
|
||||||
maybe_consume_object_trailing_whitespace_if_not_exiting(context, remaining)?;
|
maybe_consume_object_trailing_whitespace_if_not_exiting(context, remaining)?;
|
||||||
|
|
||||||
let source = get_consumed(input, remaining);
|
let source = get_consumed(input, remaining);
|
||||||
@@ -47,6 +47,7 @@ pub(crate) fn org_macro<'b, 'g, 'r, 's>(
|
|||||||
.map(|arg| arg.into())
|
.map(|arg| arg.into())
|
||||||
.collect(),
|
.collect(),
|
||||||
value: Into::<&str>::into(macro_value),
|
value: Into::<&str>::into(macro_value),
|
||||||
|
post_blank: post_blank.map(Into::<&str>::into),
|
||||||
},
|
},
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ use nom::Slice;
|
|||||||
|
|
||||||
pub(crate) type BracketDepth = i16;
|
pub(crate) type BracketDepth = i16;
|
||||||
|
|
||||||
#[derive(Copy, Clone, PartialEq)]
|
#[derive(Copy, Clone)]
|
||||||
pub(crate) struct OrgSource<'s> {
|
pub(crate) struct OrgSource<'s> {
|
||||||
full_source: &'s str,
|
full_source: &'s str,
|
||||||
start: usize,
|
start: usize,
|
||||||
|
|||||||
@@ -1,5 +1,8 @@
|
|||||||
use nom::branch::alt;
|
use nom::branch::alt;
|
||||||
|
use nom::character::complete::space1;
|
||||||
|
use nom::combinator::consumed;
|
||||||
use nom::combinator::eof;
|
use nom::combinator::eof;
|
||||||
|
use nom::combinator::opt;
|
||||||
use nom::combinator::recognize;
|
use nom::combinator::recognize;
|
||||||
use nom::combinator::verify;
|
use nom::combinator::verify;
|
||||||
use nom::multi::many1;
|
use nom::multi::many1;
|
||||||
@@ -12,6 +15,7 @@ use super::org_source::OrgSource;
|
|||||||
use super::util::blank_line;
|
use super::util::blank_line;
|
||||||
use super::util::get_consumed;
|
use super::util::get_consumed;
|
||||||
use super::util::maybe_consume_trailing_whitespace_if_not_exiting;
|
use super::util::maybe_consume_trailing_whitespace_if_not_exiting;
|
||||||
|
use super::util::org_line_ending;
|
||||||
use crate::context::parser_with_context;
|
use crate::context::parser_with_context;
|
||||||
use crate::context::ContextElement;
|
use crate::context::ContextElement;
|
||||||
use crate::context::ExitClass;
|
use crate::context::ExitClass;
|
||||||
@@ -45,14 +49,14 @@ where
|
|||||||
let standard_set_object_matcher = parser_with_context!(standard_set_object)(&parser_context);
|
let standard_set_object_matcher = parser_with_context!(standard_set_object)(&parser_context);
|
||||||
let exit_matcher = parser_with_context!(exit_matcher_parser)(&parser_context);
|
let exit_matcher = parser_with_context!(exit_matcher_parser)(&parser_context);
|
||||||
|
|
||||||
let (remaining, (children, _exit_contents)) = verify(
|
let (remaining, (contents, (children, _exit_contents))) = consumed(verify(
|
||||||
many_till(standard_set_object_matcher, exit_matcher),
|
many_till(standard_set_object_matcher, exit_matcher),
|
||||||
|(children, _exit_contents)| !children.is_empty(),
|
|(children, _exit_contents)| !children.is_empty(),
|
||||||
)(remaining)?;
|
))(remaining)?;
|
||||||
|
|
||||||
// Not checking parent exit matcher because if there are any children matched then we have a valid paragraph.
|
// Not checking parent exit matcher because if there are any children matched then we have a valid paragraph.
|
||||||
|
|
||||||
let (remaining, _trailing_ws) =
|
let (remaining, post_blank) =
|
||||||
maybe_consume_trailing_whitespace_if_not_exiting(context, remaining)?;
|
maybe_consume_trailing_whitespace_if_not_exiting(context, remaining)?;
|
||||||
let source = get_consumed(input, remaining);
|
let source = get_consumed(input, remaining);
|
||||||
|
|
||||||
@@ -60,6 +64,8 @@ where
|
|||||||
remaining,
|
remaining,
|
||||||
Paragraph {
|
Paragraph {
|
||||||
source: source.into(),
|
source: source.into(),
|
||||||
|
contents: Some(contents.into()),
|
||||||
|
post_blank: post_blank.map(Into::<&str>::into),
|
||||||
affiliated_keywords: parse_affiliated_keywords(
|
affiliated_keywords: parse_affiliated_keywords(
|
||||||
context.get_global_settings(),
|
context.get_global_settings(),
|
||||||
affiliated_keywords,
|
affiliated_keywords,
|
||||||
@@ -69,6 +75,57 @@ where
|
|||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg_attr(
|
||||||
|
feature = "tracing",
|
||||||
|
tracing::instrument(ret, level = "debug", skip(context))
|
||||||
|
)]
|
||||||
|
pub(crate) fn empty_paragraph<'b, 'g, 'r, 's>(
|
||||||
|
context: RefContext<'b, 'g, 'r, 's>,
|
||||||
|
input: OrgSource<'s>,
|
||||||
|
) -> Res<OrgSource<'s>, Paragraph<'s>> {
|
||||||
|
// If it is just a single newline then source, contents, and post-blank are "\n".
|
||||||
|
// If it has multiple newlines then contents is the first "\n" and post-blank is all the new lines.
|
||||||
|
// If there are any spaces on the first line then post-blank excludes the first line.
|
||||||
|
|
||||||
|
let exit_matcher = parser_with_context!(exit_matcher_parser)(context);
|
||||||
|
|
||||||
|
let (remaining, first_line_with_spaces) =
|
||||||
|
opt(recognize(tuple((space1, org_line_ending))))(input)?;
|
||||||
|
|
||||||
|
let post_blank_begin = remaining;
|
||||||
|
|
||||||
|
if let Some(first_line_with_spaces) = first_line_with_spaces {
|
||||||
|
let (remaining, _additional_lines) =
|
||||||
|
recognize(many_till(blank_line, exit_matcher))(remaining)?;
|
||||||
|
let post_blank = get_consumed(post_blank_begin, remaining);
|
||||||
|
let source = get_consumed(input, remaining);
|
||||||
|
Ok((
|
||||||
|
remaining,
|
||||||
|
Paragraph::of_text(
|
||||||
|
Into::<&str>::into(source),
|
||||||
|
Into::<&str>::into(first_line_with_spaces),
|
||||||
|
Some(Into::<&str>::into(first_line_with_spaces)),
|
||||||
|
Some(Into::<&str>::into(post_blank)),
|
||||||
|
),
|
||||||
|
))
|
||||||
|
} else {
|
||||||
|
let (remaining, first_line) = blank_line(remaining)?;
|
||||||
|
let (remaining, _additional_lines) =
|
||||||
|
recognize(many_till(blank_line, exit_matcher))(remaining)?;
|
||||||
|
let post_blank = get_consumed(post_blank_begin, remaining);
|
||||||
|
let source = get_consumed(input, remaining);
|
||||||
|
Ok((
|
||||||
|
remaining,
|
||||||
|
Paragraph::of_text(
|
||||||
|
Into::<&str>::into(source),
|
||||||
|
Into::<&str>::into(first_line),
|
||||||
|
Some(Into::<&str>::into(first_line)),
|
||||||
|
Some(Into::<&str>::into(post_blank)),
|
||||||
|
),
|
||||||
|
))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg_attr(
|
#[cfg_attr(
|
||||||
feature = "tracing",
|
feature = "tracing",
|
||||||
tracing::instrument(ret, level = "debug", skip(context))
|
tracing::instrument(ret, level = "debug", skip(context))
|
||||||
@@ -96,7 +153,8 @@ mod tests {
|
|||||||
use crate::context::List;
|
use crate::context::List;
|
||||||
use crate::parser::element_parser::element;
|
use crate::parser::element_parser::element;
|
||||||
use crate::parser::org_source::OrgSource;
|
use crate::parser::org_source::OrgSource;
|
||||||
use crate::types::GetStandardProperties;
|
use crate::parser::paragraph::empty_paragraph;
|
||||||
|
use crate::types::StandardProperties;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn two_paragraphs() {
|
fn two_paragraphs() {
|
||||||
@@ -109,13 +167,20 @@ mod tests {
|
|||||||
let (remaining, second_paragraph) =
|
let (remaining, second_paragraph) =
|
||||||
paragraph_matcher(remaining).expect("Parse second paragraph.");
|
paragraph_matcher(remaining).expect("Parse second paragraph.");
|
||||||
assert_eq!(Into::<&str>::into(remaining), "");
|
assert_eq!(Into::<&str>::into(remaining), "");
|
||||||
assert_eq!(
|
assert_eq!(first_paragraph.get_source(), "foo bar baz\n\n");
|
||||||
first_paragraph.get_standard_properties().get_source(),
|
assert_eq!(second_paragraph.get_source(), "lorem ipsum");
|
||||||
"foo bar baz\n\n"
|
}
|
||||||
);
|
|
||||||
assert_eq!(
|
#[test]
|
||||||
second_paragraph.get_standard_properties().get_source(),
|
fn paragraph_whitespace() {
|
||||||
"lorem ipsum"
|
let input = OrgSource::new("\n");
|
||||||
);
|
let global_settings = GlobalSettings::default();
|
||||||
|
let initial_context = ContextElement::document_context();
|
||||||
|
let initial_context = Context::new(&global_settings, List::new(&initial_context));
|
||||||
|
let paragraph_matcher = bind_context!(empty_paragraph, &initial_context);
|
||||||
|
let (remaining, paragraph) = paragraph_matcher(input).expect("Parse paragraph");
|
||||||
|
assert_eq!(Into::<&str>::into(remaining), "");
|
||||||
|
assert_eq!(paragraph.get_source(), "\n");
|
||||||
|
assert_eq!(paragraph.get_contents(), Some("\n"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -54,7 +54,7 @@ pub(crate) fn plain_link<'b, 'g, 'r, 's>(
|
|||||||
let (remaining, _) = pre(context, input)?;
|
let (remaining, _) = pre(context, input)?;
|
||||||
let (remaining, path_plain) = parse_path_plain(context, remaining)?;
|
let (remaining, path_plain) = parse_path_plain(context, remaining)?;
|
||||||
peek(parser_with_context!(post)(context))(remaining)?;
|
peek(parser_with_context!(post)(context))(remaining)?;
|
||||||
let (remaining, _trailing_whitespace) =
|
let (remaining, post_blank) =
|
||||||
maybe_consume_object_trailing_whitespace_if_not_exiting(context, remaining)?;
|
maybe_consume_object_trailing_whitespace_if_not_exiting(context, remaining)?;
|
||||||
let source = get_consumed(input, remaining);
|
let source = get_consumed(input, remaining);
|
||||||
Ok((
|
Ok((
|
||||||
@@ -66,6 +66,7 @@ pub(crate) fn plain_link<'b, 'g, 'r, 's>(
|
|||||||
raw_link: path_plain.raw_link,
|
raw_link: path_plain.raw_link,
|
||||||
search_option: path_plain.search_option,
|
search_option: path_plain.search_option,
|
||||||
application: path_plain.application,
|
application: path_plain.application,
|
||||||
|
post_blank: post_blank.map(Into::<&str>::into),
|
||||||
},
|
},
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ use nom::character::complete::multispace1;
|
|||||||
use nom::character::complete::one_of;
|
use nom::character::complete::one_of;
|
||||||
use nom::character::complete::space0;
|
use nom::character::complete::space0;
|
||||||
use nom::character::complete::space1;
|
use nom::character::complete::space1;
|
||||||
|
use nom::combinator::consumed;
|
||||||
use nom::combinator::eof;
|
use nom::combinator::eof;
|
||||||
use nom::combinator::map;
|
use nom::combinator::map;
|
||||||
use nom::combinator::not;
|
use nom::combinator::not;
|
||||||
@@ -152,6 +153,7 @@ where
|
|||||||
let mut children = Vec::new();
|
let mut children = Vec::new();
|
||||||
let mut first_item_indentation: Option<IndentationLevel> = None;
|
let mut first_item_indentation: Option<IndentationLevel> = None;
|
||||||
let mut first_item_list_type: Option<PlainListType> = None;
|
let mut first_item_list_type: Option<PlainListType> = None;
|
||||||
|
let contents_begin = remaining;
|
||||||
let mut remaining = remaining;
|
let mut remaining = remaining;
|
||||||
|
|
||||||
// The final list item does not consume trailing blank lines (which instead get consumed by the list). We have three options here:
|
// The final list item does not consume trailing blank lines (which instead get consumed by the list). We have three options here:
|
||||||
@@ -195,7 +197,8 @@ where
|
|||||||
)));
|
)));
|
||||||
}
|
}
|
||||||
|
|
||||||
let (remaining, _trailing_ws) =
|
let contents = get_consumed(contents_begin, remaining);
|
||||||
|
let (remaining, post_blank) =
|
||||||
maybe_consume_trailing_whitespace_if_not_exiting(context, remaining)?;
|
maybe_consume_trailing_whitespace_if_not_exiting(context, remaining)?;
|
||||||
let source = get_consumed(input, remaining);
|
let source = get_consumed(input, remaining);
|
||||||
Ok((
|
Ok((
|
||||||
@@ -208,6 +211,8 @@ where
|
|||||||
),
|
),
|
||||||
list_type: first_item_list_type.expect("Plain lists require at least one element."),
|
list_type: first_item_list_type.expect("Plain lists require at least one element."),
|
||||||
children: children.into_iter().map(|(_start, item)| item).collect(),
|
children: children.into_iter().map(|(_start, item)| item).collect(),
|
||||||
|
contents: Some(Into::<&str>::into(contents)),
|
||||||
|
post_blank: post_blank.map(Into::<&str>::into),
|
||||||
},
|
},
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
@@ -265,7 +270,7 @@ fn plain_list_item<'b, 'g, 'r, 's>(
|
|||||||
let maybe_contentless_item: Res<OrgSource<'_>, ()> =
|
let maybe_contentless_item: Res<OrgSource<'_>, ()> =
|
||||||
detect_contentless_item_contents(&parser_context, remaining);
|
detect_contentless_item_contents(&parser_context, remaining);
|
||||||
if let Ok((_rem, _ws)) = maybe_contentless_item {
|
if let Ok((_rem, _ws)) = maybe_contentless_item {
|
||||||
let (remaining, _trailing_ws) = if tuple((
|
let (remaining, post_blank) = if tuple((
|
||||||
blank_line,
|
blank_line,
|
||||||
bind_context!(final_item_whitespace_cutoff, context),
|
bind_context!(final_item_whitespace_cutoff, context),
|
||||||
))(remaining)
|
))(remaining)
|
||||||
@@ -291,6 +296,12 @@ fn plain_list_item<'b, 'g, 'r, 's>(
|
|||||||
.unwrap_or(Vec::new()),
|
.unwrap_or(Vec::new()),
|
||||||
pre_blank: 0,
|
pre_blank: 0,
|
||||||
children: Vec::new(),
|
children: Vec::new(),
|
||||||
|
contents: None,
|
||||||
|
post_blank: if post_blank.len() > 0 {
|
||||||
|
Some(Into::<&str>::into(post_blank))
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
},
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
));
|
));
|
||||||
@@ -301,13 +312,13 @@ fn plain_list_item<'b, 'g, 'r, 's>(
|
|||||||
.filter(|b| *b == b'\n')
|
.filter(|b| *b == b'\n')
|
||||||
.count();
|
.count();
|
||||||
|
|
||||||
let (remaining, (children, _exit_contents)) = many_till(
|
let (remaining, (contents, (children, _exit_contents))) = consumed(many_till(
|
||||||
include_input(bind_context!(element(true), &parser_context)),
|
include_input(bind_context!(element(true), &parser_context)),
|
||||||
bind_context!(exit_matcher_parser, &parser_context),
|
bind_context!(exit_matcher_parser, &parser_context),
|
||||||
)(remaining)?;
|
))(remaining)?;
|
||||||
|
|
||||||
// We have to use the parser_context here to include the whitespace cut-off
|
// We have to use the parser_context here to include the whitespace cut-off
|
||||||
let (remaining, _trailing_ws) =
|
let (remaining, post_blank) =
|
||||||
maybe_consume_trailing_whitespace_if_not_exiting(&final_whitespace_context, remaining)?;
|
maybe_consume_trailing_whitespace_if_not_exiting(&final_whitespace_context, remaining)?;
|
||||||
|
|
||||||
let source = get_consumed(input, remaining);
|
let source = get_consumed(input, remaining);
|
||||||
@@ -329,6 +340,12 @@ fn plain_list_item<'b, 'g, 'r, 's>(
|
|||||||
pre_blank: PlainListItemPreBlank::try_from(pre_blank)
|
pre_blank: PlainListItemPreBlank::try_from(pre_blank)
|
||||||
.expect("pre-blank cannot be larger than 2."),
|
.expect("pre-blank cannot be larger than 2."),
|
||||||
children: children.into_iter().map(|(_start, item)| item).collect(),
|
children: children.into_iter().map(|(_start, item)| item).collect(),
|
||||||
|
contents: if contents.len() > 0 {
|
||||||
|
Some(contents.into())
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
},
|
||||||
|
post_blank: post_blank.map(Into::<&str>::into),
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
));
|
));
|
||||||
@@ -629,7 +646,7 @@ mod tests {
|
|||||||
use crate::context::Context;
|
use crate::context::Context;
|
||||||
use crate::context::GlobalSettings;
|
use crate::context::GlobalSettings;
|
||||||
use crate::context::List;
|
use crate::context::List;
|
||||||
use crate::types::GetStandardProperties;
|
use crate::types::StandardProperties;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn plain_list_item_empty() {
|
fn plain_list_item_empty() {
|
||||||
@@ -640,7 +657,7 @@ mod tests {
|
|||||||
let plain_list_item_matcher = bind_context!(plain_list_item, &initial_context);
|
let plain_list_item_matcher = bind_context!(plain_list_item, &initial_context);
|
||||||
let (remaining, (_, result)) = plain_list_item_matcher(input).unwrap();
|
let (remaining, (_, result)) = plain_list_item_matcher(input).unwrap();
|
||||||
assert_eq!(Into::<&str>::into(remaining), "");
|
assert_eq!(Into::<&str>::into(remaining), "");
|
||||||
assert_eq!(result.get_standard_properties().get_source(), "1.");
|
assert_eq!(result.get_source(), "1.");
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@@ -652,7 +669,7 @@ mod tests {
|
|||||||
let plain_list_item_matcher = bind_context!(plain_list_item, &initial_context);
|
let plain_list_item_matcher = bind_context!(plain_list_item, &initial_context);
|
||||||
let (remaining, (_, result)) = plain_list_item_matcher(input).unwrap();
|
let (remaining, (_, result)) = plain_list_item_matcher(input).unwrap();
|
||||||
assert_eq!(Into::<&str>::into(remaining), "");
|
assert_eq!(Into::<&str>::into(remaining), "");
|
||||||
assert_eq!(result.get_standard_properties().get_source(), "1. foo");
|
assert_eq!(result.get_source(), "1. foo");
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@@ -664,7 +681,7 @@ mod tests {
|
|||||||
let (remaining, result) =
|
let (remaining, result) =
|
||||||
plain_list(std::iter::empty(), input, &initial_context, input).unwrap();
|
plain_list(std::iter::empty(), input, &initial_context, input).unwrap();
|
||||||
assert_eq!(Into::<&str>::into(remaining), "");
|
assert_eq!(Into::<&str>::into(remaining), "");
|
||||||
assert_eq!(result.get_standard_properties().get_source(), "1.");
|
assert_eq!(result.get_source(), "1.");
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@@ -676,7 +693,7 @@ mod tests {
|
|||||||
let (remaining, result) =
|
let (remaining, result) =
|
||||||
plain_list(std::iter::empty(), input, &initial_context, input).unwrap();
|
plain_list(std::iter::empty(), input, &initial_context, input).unwrap();
|
||||||
assert_eq!(Into::<&str>::into(remaining), "");
|
assert_eq!(Into::<&str>::into(remaining), "");
|
||||||
assert_eq!(result.get_standard_properties().get_source(), "1. foo");
|
assert_eq!(result.get_source(), "1. foo");
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@@ -721,7 +738,7 @@ mod tests {
|
|||||||
plain_list_matcher(input).expect("Should parse the plain list successfully.");
|
plain_list_matcher(input).expect("Should parse the plain list successfully.");
|
||||||
assert_eq!(Into::<&str>::into(remaining), " ipsum\n");
|
assert_eq!(Into::<&str>::into(remaining), " ipsum\n");
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
result.get_standard_properties().get_source(),
|
result.get_source(),
|
||||||
r#"1. foo
|
r#"1. foo
|
||||||
2. bar
|
2. bar
|
||||||
baz
|
baz
|
||||||
@@ -749,7 +766,7 @@ baz"#,
|
|||||||
plain_list_matcher(input).expect("Should parse the plain list successfully.");
|
plain_list_matcher(input).expect("Should parse the plain list successfully.");
|
||||||
assert_eq!(Into::<&str>::into(remaining), "baz");
|
assert_eq!(Into::<&str>::into(remaining), "baz");
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
result.get_standard_properties().get_source(),
|
result.get_source(),
|
||||||
r#"1. foo
|
r#"1. foo
|
||||||
1. bar
|
1. bar
|
||||||
|
|
||||||
@@ -782,7 +799,7 @@ dolar"#,
|
|||||||
plain_list_matcher(input).expect("Should parse the plain list successfully.");
|
plain_list_matcher(input).expect("Should parse the plain list successfully.");
|
||||||
assert_eq!(Into::<&str>::into(remaining), "dolar");
|
assert_eq!(Into::<&str>::into(remaining), "dolar");
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
result.get_standard_properties().get_source(),
|
result.get_source(),
|
||||||
r#"1. foo
|
r#"1. foo
|
||||||
|
|
||||||
bar
|
bar
|
||||||
|
|||||||
@@ -143,7 +143,7 @@ mod tests {
|
|||||||
use crate::context::GlobalSettings;
|
use crate::context::GlobalSettings;
|
||||||
use crate::context::List;
|
use crate::context::List;
|
||||||
use crate::parser::object_parser::detect_standard_set_object_sans_plain_text;
|
use crate::parser::object_parser::detect_standard_set_object_sans_plain_text;
|
||||||
use crate::types::GetStandardProperties;
|
use crate::types::StandardProperties;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn plain_text_simple() {
|
fn plain_text_simple() {
|
||||||
@@ -160,9 +160,6 @@ mod tests {
|
|||||||
)(input)
|
)(input)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
assert_eq!(Into::<&str>::into(remaining), "");
|
assert_eq!(Into::<&str>::into(remaining), "");
|
||||||
assert_eq!(
|
assert_eq!(result.get_source(), Into::<&str>::into(input));
|
||||||
result.get_standard_properties().get_source(),
|
|
||||||
Into::<&str>::into(input)
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -33,7 +33,7 @@ pub(crate) fn planning<'b, 'g, 'r, 's>(
|
|||||||
many1(parser_with_context!(planning_parameter)(context))(remaining)?;
|
many1(parser_with_context!(planning_parameter)(context))(remaining)?;
|
||||||
let (remaining, _trailing_ws) = tuple((space0, org_line_ending))(remaining)?;
|
let (remaining, _trailing_ws) = tuple((space0, org_line_ending))(remaining)?;
|
||||||
|
|
||||||
let (remaining, _trailing_ws) =
|
let (remaining, post_blank) =
|
||||||
maybe_consume_trailing_whitespace_if_not_exiting(context, remaining)?;
|
maybe_consume_trailing_whitespace_if_not_exiting(context, remaining)?;
|
||||||
let source = get_consumed(input, remaining);
|
let source = get_consumed(input, remaining);
|
||||||
|
|
||||||
@@ -62,6 +62,7 @@ pub(crate) fn planning<'b, 'g, 'r, 's>(
|
|||||||
scheduled,
|
scheduled,
|
||||||
deadline,
|
deadline,
|
||||||
closed,
|
closed,
|
||||||
|
post_blank: post_blank.map(Into::<&str>::into),
|
||||||
},
|
},
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ use nom::character::complete::anychar;
|
|||||||
use nom::character::complete::line_ending;
|
use nom::character::complete::line_ending;
|
||||||
use nom::character::complete::space0;
|
use nom::character::complete::space0;
|
||||||
use nom::character::complete::space1;
|
use nom::character::complete::space1;
|
||||||
|
use nom::combinator::consumed;
|
||||||
use nom::combinator::eof;
|
use nom::combinator::eof;
|
||||||
use nom::combinator::opt;
|
use nom::combinator::opt;
|
||||||
use nom::combinator::recognize;
|
use nom::combinator::recognize;
|
||||||
@@ -64,14 +65,11 @@ pub(crate) fn property_drawer<'b, 'g, 'r, 's>(
|
|||||||
let parser_context = context.with_additional_node(&contexts[0]);
|
let parser_context = context.with_additional_node(&contexts[0]);
|
||||||
let parser_context = parser_context.with_additional_node(&contexts[1]);
|
let parser_context = parser_context.with_additional_node(&contexts[1]);
|
||||||
let parser_context = parser_context.with_additional_node(&contexts[2]);
|
let parser_context = parser_context.with_additional_node(&contexts[2]);
|
||||||
|
let (remaining, (contents, children)) =
|
||||||
let node_property_matcher = parser_with_context!(node_property)(&parser_context);
|
consumed(parser_with_context!(children)(&parser_context))(remaining)?;
|
||||||
let exit_matcher = parser_with_context!(exit_matcher_parser)(&parser_context);
|
|
||||||
let (remaining, (children, _exit_contents)) =
|
|
||||||
many_till(node_property_matcher, exit_matcher)(remaining)?;
|
|
||||||
let (remaining, _end) = property_drawer_end(&parser_context, remaining)?;
|
let (remaining, _end) = property_drawer_end(&parser_context, remaining)?;
|
||||||
|
|
||||||
let (remaining, _trailing_ws) =
|
let (remaining, post_blank) =
|
||||||
maybe_consume_trailing_whitespace_if_not_exiting(context, remaining)?;
|
maybe_consume_trailing_whitespace_if_not_exiting(context, remaining)?;
|
||||||
let source = get_consumed(input, remaining);
|
let source = get_consumed(input, remaining);
|
||||||
|
|
||||||
@@ -80,10 +78,31 @@ pub(crate) fn property_drawer<'b, 'g, 'r, 's>(
|
|||||||
PropertyDrawer {
|
PropertyDrawer {
|
||||||
source: source.into(),
|
source: source.into(),
|
||||||
children,
|
children,
|
||||||
|
contents: if contents.len() > 0 {
|
||||||
|
Some(contents.into())
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
},
|
||||||
|
post_blank: post_blank.map(Into::<&str>::into),
|
||||||
},
|
},
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg_attr(
|
||||||
|
feature = "tracing",
|
||||||
|
tracing::instrument(ret, level = "debug", skip(context))
|
||||||
|
)]
|
||||||
|
fn children<'b, 'g, 'r, 's>(
|
||||||
|
context: RefContext<'b, 'g, 'r, 's>,
|
||||||
|
input: OrgSource<'s>,
|
||||||
|
) -> Res<OrgSource<'s>, Vec<NodeProperty<'s>>> {
|
||||||
|
let node_property_matcher = parser_with_context!(node_property)(context);
|
||||||
|
let exit_matcher = parser_with_context!(exit_matcher_parser)(context);
|
||||||
|
let (remaining, (children, _exit_contents)) =
|
||||||
|
many_till(node_property_matcher, exit_matcher)(input)?;
|
||||||
|
Ok((remaining, children))
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg_attr(
|
#[cfg_attr(
|
||||||
feature = "tracing",
|
feature = "tracing",
|
||||||
tracing::instrument(ret, level = "debug", skip(_context))
|
tracing::instrument(ret, level = "debug", skip(_context))
|
||||||
|
|||||||
@@ -39,7 +39,7 @@ pub(crate) fn radio_link<'b, 'g, 'r, 's>(
|
|||||||
let rematched_target = rematch_target(context, radio_target, input);
|
let rematched_target = rematch_target(context, radio_target, input);
|
||||||
if let Ok((remaining, rematched_target)) = rematched_target {
|
if let Ok((remaining, rematched_target)) = rematched_target {
|
||||||
let path = get_consumed(input, remaining);
|
let path = get_consumed(input, remaining);
|
||||||
let (remaining, _) = space0(remaining)?;
|
let (remaining, post_blank) = space0(remaining)?;
|
||||||
let source = get_consumed(input, remaining);
|
let source = get_consumed(input, remaining);
|
||||||
return Ok((
|
return Ok((
|
||||||
remaining,
|
remaining,
|
||||||
@@ -47,6 +47,11 @@ pub(crate) fn radio_link<'b, 'g, 'r, 's>(
|
|||||||
source: source.into(),
|
source: source.into(),
|
||||||
children: rematched_target,
|
children: rematched_target,
|
||||||
path: path.into(),
|
path: path.into(),
|
||||||
|
post_blank: if post_blank.len() > 0 {
|
||||||
|
Some(Into::<&str>::into(post_blank))
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
},
|
||||||
},
|
},
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
@@ -134,7 +139,7 @@ pub(crate) fn radio_target<'b, 'g, 'r, 's>(
|
|||||||
))(remaining)?;
|
))(remaining)?;
|
||||||
|
|
||||||
let (remaining, _closing) = tag(">>>")(remaining)?;
|
let (remaining, _closing) = tag(">>>")(remaining)?;
|
||||||
let (remaining, _trailing_whitespace) =
|
let (remaining, post_blank) =
|
||||||
maybe_consume_object_trailing_whitespace_if_not_exiting(context, remaining)?;
|
maybe_consume_object_trailing_whitespace_if_not_exiting(context, remaining)?;
|
||||||
let source = get_consumed(input, remaining);
|
let source = get_consumed(input, remaining);
|
||||||
Ok((
|
Ok((
|
||||||
@@ -142,6 +147,7 @@ pub(crate) fn radio_target<'b, 'g, 'r, 's>(
|
|||||||
RadioTarget {
|
RadioTarget {
|
||||||
source: source.into(),
|
source: source.into(),
|
||||||
value: raw_value.into(),
|
value: raw_value.into(),
|
||||||
|
post_blank: post_blank.map(Into::<&str>::into),
|
||||||
children,
|
children,
|
||||||
},
|
},
|
||||||
))
|
))
|
||||||
@@ -175,11 +181,11 @@ mod tests {
|
|||||||
use crate::parser::element_parser::element;
|
use crate::parser::element_parser::element;
|
||||||
use crate::types::Bold;
|
use crate::types::Bold;
|
||||||
use crate::types::Element;
|
use crate::types::Element;
|
||||||
use crate::types::GetStandardProperties;
|
|
||||||
use crate::types::PlainText;
|
use crate::types::PlainText;
|
||||||
|
use crate::types::StandardProperties;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn plain_text_radio_target() {
|
fn plain_text_radio_target() -> Result<(), Box<dyn std::error::Error>> {
|
||||||
let input = OrgSource::new("foo bar baz");
|
let input = OrgSource::new("foo bar baz");
|
||||||
let radio_target_match = vec![Object::PlainText(PlainText { source: "bar" })];
|
let radio_target_match = vec![Object::PlainText(PlainText { source: "bar" })];
|
||||||
let global_settings = GlobalSettings {
|
let global_settings = GlobalSettings {
|
||||||
@@ -195,29 +201,38 @@ mod tests {
|
|||||||
_ => panic!("Should be a paragraph!"),
|
_ => panic!("Should be a paragraph!"),
|
||||||
};
|
};
|
||||||
assert_eq!(Into::<&str>::into(remaining), "");
|
assert_eq!(Into::<&str>::into(remaining), "");
|
||||||
assert_eq!(
|
assert_eq!(first_paragraph.get_source(), "foo bar baz");
|
||||||
first_paragraph.get_standard_properties().get_source(),
|
|
||||||
"foo bar baz"
|
|
||||||
);
|
|
||||||
assert_eq!(first_paragraph.children.len(), 3);
|
assert_eq!(first_paragraph.children.len(), 3);
|
||||||
assert_eq!(
|
match first_paragraph
|
||||||
first_paragraph
|
|
||||||
.children
|
.children
|
||||||
.get(1)
|
.get(1)
|
||||||
.expect("Len already asserted to be 3"),
|
.expect("Len already asserted to be 3.")
|
||||||
&Object::RadioLink(RadioLink {
|
{
|
||||||
source: "bar ",
|
Object::RadioLink(inner) => {
|
||||||
children: vec![Object::PlainText(PlainText { source: "bar" })],
|
assert_eq!(inner.get_source(), "bar ");
|
||||||
path: "bar"
|
assert_eq!(inner.path, "bar");
|
||||||
})
|
assert_eq!(inner.children.len(), 1);
|
||||||
);
|
let child = inner
|
||||||
|
.children
|
||||||
|
.first()
|
||||||
|
.expect("Length already asserted to be 1.");
|
||||||
|
assert!(matches!(child, Object::PlainText(_)));
|
||||||
|
assert_eq!(child.get_source(), "bar");
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
return Err("Child should be a radio link.".into());
|
||||||
|
}
|
||||||
|
};
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn bold_radio_target() {
|
fn bold_radio_target() -> Result<(), Box<dyn std::error::Error>> {
|
||||||
let input = OrgSource::new("foo *bar* baz");
|
let input = OrgSource::new("foo *bar* baz");
|
||||||
let radio_target_match = vec![Object::Bold(Bold {
|
let radio_target_match = vec![Object::Bold(Bold {
|
||||||
source: "*bar*",
|
source: "*bar*",
|
||||||
|
contents: "bar",
|
||||||
|
post_blank: Some(" "),
|
||||||
children: vec![Object::PlainText(PlainText { source: "bar" })],
|
children: vec![Object::PlainText(PlainText { source: "bar" })],
|
||||||
})];
|
})];
|
||||||
let global_settings = GlobalSettings {
|
let global_settings = GlobalSettings {
|
||||||
@@ -234,24 +249,28 @@ mod tests {
|
|||||||
_ => panic!("Should be a paragraph!"),
|
_ => panic!("Should be a paragraph!"),
|
||||||
};
|
};
|
||||||
assert_eq!(Into::<&str>::into(remaining), "");
|
assert_eq!(Into::<&str>::into(remaining), "");
|
||||||
assert_eq!(
|
assert_eq!(first_paragraph.get_source(), "foo *bar* baz");
|
||||||
first_paragraph.get_standard_properties().get_source(),
|
|
||||||
"foo *bar* baz"
|
|
||||||
);
|
|
||||||
assert_eq!(first_paragraph.children.len(), 3);
|
assert_eq!(first_paragraph.children.len(), 3);
|
||||||
assert_eq!(
|
match first_paragraph
|
||||||
first_paragraph
|
|
||||||
.children
|
.children
|
||||||
.get(1)
|
.get(1)
|
||||||
.expect("Len already asserted to be 3"),
|
.expect("Len already asserted to be 3.")
|
||||||
&Object::RadioLink(RadioLink {
|
{
|
||||||
source: "*bar* ",
|
Object::RadioLink(inner) => {
|
||||||
children: vec![Object::Bold(Bold {
|
assert_eq!(inner.get_source(), "*bar* ");
|
||||||
source: "*bar* ",
|
assert_eq!(inner.path, "*bar* ");
|
||||||
children: vec![Object::PlainText(PlainText { source: "bar" })]
|
assert_eq!(inner.children.len(), 1);
|
||||||
})],
|
let child = inner
|
||||||
path: "*bar* "
|
.children
|
||||||
})
|
.first()
|
||||||
);
|
.expect("Length already asserted to be 1.");
|
||||||
|
assert!(matches!(child, Object::Bold(_)));
|
||||||
|
assert_eq!(child.get_source(), "*bar* ");
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
return Err("Child should be a radio link.".into());
|
||||||
|
}
|
||||||
|
};
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -73,7 +73,7 @@ fn regular_link_without_description<'b, 'g, 'r, 's>(
|
|||||||
let (remaining, _opening_bracket) = tag("[[")(input)?;
|
let (remaining, _opening_bracket) = tag("[[")(input)?;
|
||||||
let (remaining, path) = pathreg(context, remaining)?;
|
let (remaining, path) = pathreg(context, remaining)?;
|
||||||
let (remaining, _closing_bracket) = tag("]]")(remaining)?;
|
let (remaining, _closing_bracket) = tag("]]")(remaining)?;
|
||||||
let (remaining, _trailing_whitespace) =
|
let (remaining, post_blank) =
|
||||||
maybe_consume_object_trailing_whitespace_if_not_exiting(context, remaining)?;
|
maybe_consume_object_trailing_whitespace_if_not_exiting(context, remaining)?;
|
||||||
let source = get_consumed(input, remaining);
|
let source = get_consumed(input, remaining);
|
||||||
Ok((
|
Ok((
|
||||||
@@ -84,6 +84,8 @@ fn regular_link_without_description<'b, 'g, 'r, 's>(
|
|||||||
path: path.path,
|
path: path.path,
|
||||||
raw_link: path.raw_link,
|
raw_link: path.raw_link,
|
||||||
search_option: path.search_option,
|
search_option: path.search_option,
|
||||||
|
contents: None,
|
||||||
|
post_blank: post_blank.map(Into::<&str>::into),
|
||||||
children: Vec::new(),
|
children: Vec::new(),
|
||||||
application: path.application,
|
application: path.application,
|
||||||
},
|
},
|
||||||
@@ -101,9 +103,10 @@ fn regular_link_with_description<'b, 'g, 'r, 's>(
|
|||||||
let (remaining, _opening_bracket) = tag("[[")(input)?;
|
let (remaining, _opening_bracket) = tag("[[")(input)?;
|
||||||
let (remaining, path) = pathreg(context, remaining)?;
|
let (remaining, path) = pathreg(context, remaining)?;
|
||||||
let (remaining, _closing_bracket) = tag("][")(remaining)?;
|
let (remaining, _closing_bracket) = tag("][")(remaining)?;
|
||||||
let (remaining, description) = description(context, remaining)?;
|
let (remaining, (contents, description)) =
|
||||||
|
consumed(parser_with_context!(description)(context))(remaining)?;
|
||||||
let (remaining, _closing_bracket) = tag("]]")(remaining)?;
|
let (remaining, _closing_bracket) = tag("]]")(remaining)?;
|
||||||
let (remaining, _trailing_whitespace) =
|
let (remaining, post_blank) =
|
||||||
maybe_consume_object_trailing_whitespace_if_not_exiting(context, remaining)?;
|
maybe_consume_object_trailing_whitespace_if_not_exiting(context, remaining)?;
|
||||||
let source = get_consumed(input, remaining);
|
let source = get_consumed(input, remaining);
|
||||||
Ok((
|
Ok((
|
||||||
@@ -114,6 +117,8 @@ fn regular_link_with_description<'b, 'g, 'r, 's>(
|
|||||||
path: path.path,
|
path: path.path,
|
||||||
raw_link: path.raw_link,
|
raw_link: path.raw_link,
|
||||||
search_option: path.search_option,
|
search_option: path.search_option,
|
||||||
|
contents: Some(Into::<&str>::into(contents)),
|
||||||
|
post_blank: post_blank.map(Into::<&str>::into),
|
||||||
children: description,
|
children: description,
|
||||||
application: path.application,
|
application: path.application,
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -72,7 +72,7 @@ pub(crate) fn zeroth_section<'b, 'g, 'r, 's>(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let (remaining, _trailing_ws) =
|
let (remaining, post_blank) =
|
||||||
maybe_consume_trailing_whitespace_if_not_exiting(context, remaining)?;
|
maybe_consume_trailing_whitespace_if_not_exiting(context, remaining)?;
|
||||||
|
|
||||||
let source = get_consumed(input, remaining);
|
let source = get_consumed(input, remaining);
|
||||||
@@ -80,6 +80,7 @@ pub(crate) fn zeroth_section<'b, 'g, 'r, 's>(
|
|||||||
remaining,
|
remaining,
|
||||||
Section {
|
Section {
|
||||||
source: source.into(),
|
source: source.into(),
|
||||||
|
post_blank: post_blank.map(Into::<&str>::into),
|
||||||
children,
|
children,
|
||||||
},
|
},
|
||||||
))
|
))
|
||||||
@@ -128,7 +129,7 @@ pub(crate) fn section<'b, 'g, 'r, 's>(
|
|||||||
children.insert(0, ele)
|
children.insert(0, ele)
|
||||||
}
|
}
|
||||||
|
|
||||||
let (remaining, _trailing_ws) =
|
let (remaining, post_blank) =
|
||||||
maybe_consume_trailing_whitespace_if_not_exiting(context, remaining)?;
|
maybe_consume_trailing_whitespace_if_not_exiting(context, remaining)?;
|
||||||
|
|
||||||
let source = get_consumed(input, remaining);
|
let source = get_consumed(input, remaining);
|
||||||
@@ -136,6 +137,7 @@ pub(crate) fn section<'b, 'g, 'r, 's>(
|
|||||||
remaining,
|
remaining,
|
||||||
Section {
|
Section {
|
||||||
source: source.into(),
|
source: source.into(),
|
||||||
|
post_blank: post_blank.map(Into::<&str>::into),
|
||||||
children,
|
children,
|
||||||
},
|
},
|
||||||
))
|
))
|
||||||
|
|||||||
@@ -40,7 +40,7 @@ fn percent_statistics_cookie<'b, 'g, 'r, 's>(
|
|||||||
tag("%]"),
|
tag("%]"),
|
||||||
)))(input)?;
|
)))(input)?;
|
||||||
let value = get_consumed(input, remaining);
|
let value = get_consumed(input, remaining);
|
||||||
let (remaining, _trailing_whitespace) =
|
let (remaining, post_blank) =
|
||||||
maybe_consume_object_trailing_whitespace_if_not_exiting(context, remaining)?;
|
maybe_consume_object_trailing_whitespace_if_not_exiting(context, remaining)?;
|
||||||
let source = get_consumed(input, remaining);
|
let source = get_consumed(input, remaining);
|
||||||
Ok((
|
Ok((
|
||||||
@@ -48,6 +48,7 @@ fn percent_statistics_cookie<'b, 'g, 'r, 's>(
|
|||||||
StatisticsCookie {
|
StatisticsCookie {
|
||||||
source: source.into(),
|
source: source.into(),
|
||||||
value: value.into(),
|
value: value.into(),
|
||||||
|
post_blank: post_blank.map(Into::<&str>::into),
|
||||||
},
|
},
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
@@ -68,7 +69,7 @@ fn fraction_statistics_cookie<'b, 'g, 'r, 's>(
|
|||||||
tag("]"),
|
tag("]"),
|
||||||
)))(input)?;
|
)))(input)?;
|
||||||
let value = get_consumed(input, remaining);
|
let value = get_consumed(input, remaining);
|
||||||
let (remaining, _trailing_whitespace) =
|
let (remaining, post_blank) =
|
||||||
maybe_consume_object_trailing_whitespace_if_not_exiting(context, remaining)?;
|
maybe_consume_object_trailing_whitespace_if_not_exiting(context, remaining)?;
|
||||||
let source = get_consumed(input, remaining);
|
let source = get_consumed(input, remaining);
|
||||||
Ok((
|
Ok((
|
||||||
@@ -76,6 +77,7 @@ fn fraction_statistics_cookie<'b, 'g, 'r, 's>(
|
|||||||
StatisticsCookie {
|
StatisticsCookie {
|
||||||
source: source.into(),
|
source: source.into(),
|
||||||
value: value.into(),
|
value: value.into(),
|
||||||
|
post_blank: post_blank.map(Into::<&str>::into),
|
||||||
},
|
},
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ use nom::bytes::complete::tag;
|
|||||||
use nom::bytes::complete::take_while;
|
use nom::bytes::complete::take_while;
|
||||||
use nom::character::complete::anychar;
|
use nom::character::complete::anychar;
|
||||||
use nom::character::complete::one_of;
|
use nom::character::complete::one_of;
|
||||||
|
use nom::combinator::consumed;
|
||||||
use nom::combinator::map;
|
use nom::combinator::map;
|
||||||
use nom::combinator::not;
|
use nom::combinator::not;
|
||||||
use nom::combinator::opt;
|
use nom::combinator::opt;
|
||||||
@@ -54,17 +55,20 @@ pub(crate) fn subscript<'b, 'g, 'r, 's>(
|
|||||||
context: RefContext<'b, 'g, 'r, 's>,
|
context: RefContext<'b, 'g, 'r, 's>,
|
||||||
input: OrgSource<'s>,
|
input: OrgSource<'s>,
|
||||||
) -> Res<OrgSource<'s>, Subscript<'s>> {
|
) -> Res<OrgSource<'s>, Subscript<'s>> {
|
||||||
// We check for the underscore first before checking the pre-character as a minor optimization to avoid walking up the context tree to find the document root unnecessarily.
|
|
||||||
let (remaining, _) = tag("_")(input)?;
|
let (remaining, _) = tag("_")(input)?;
|
||||||
pre(input)?;
|
pre(input)?;
|
||||||
let (remaining, body) = script_body(context, remaining)?;
|
let (remaining, body) = script_body(context, remaining)?;
|
||||||
let (remaining, _trailing_whitespace) =
|
let (remaining, post_blank) =
|
||||||
maybe_consume_object_trailing_whitespace_if_not_exiting(context, remaining)?;
|
maybe_consume_object_trailing_whitespace_if_not_exiting(context, remaining)?;
|
||||||
let source = get_consumed(input, remaining);
|
let source = get_consumed(input, remaining);
|
||||||
|
|
||||||
let (use_brackets, body) = match body {
|
let (use_brackets, contents, body) = match body {
|
||||||
ScriptBody::Braceless(text) => (false, vec![Object::PlainText(PlainText { source: text })]),
|
ScriptBody::Braceless(text) => (
|
||||||
ScriptBody::WithBraces(body) => (true, body),
|
false,
|
||||||
|
text,
|
||||||
|
vec![Object::PlainText(PlainText { source: text })],
|
||||||
|
),
|
||||||
|
ScriptBody::WithBraces(contents, body) => (true, contents, body),
|
||||||
};
|
};
|
||||||
|
|
||||||
Ok((
|
Ok((
|
||||||
@@ -72,6 +76,8 @@ pub(crate) fn subscript<'b, 'g, 'r, 's>(
|
|||||||
Subscript {
|
Subscript {
|
||||||
source: source.into(),
|
source: source.into(),
|
||||||
use_brackets,
|
use_brackets,
|
||||||
|
contents,
|
||||||
|
post_blank: post_blank.map(Into::<&str>::into),
|
||||||
children: body,
|
children: body,
|
||||||
},
|
},
|
||||||
))
|
))
|
||||||
@@ -89,13 +95,17 @@ pub(crate) fn superscript<'b, 'g, 'r, 's>(
|
|||||||
let (remaining, _) = tag("^")(input)?;
|
let (remaining, _) = tag("^")(input)?;
|
||||||
pre(input)?;
|
pre(input)?;
|
||||||
let (remaining, body) = script_body(context, remaining)?;
|
let (remaining, body) = script_body(context, remaining)?;
|
||||||
let (remaining, _trailing_whitespace) =
|
let (remaining, post_blank) =
|
||||||
maybe_consume_object_trailing_whitespace_if_not_exiting(context, remaining)?;
|
maybe_consume_object_trailing_whitespace_if_not_exiting(context, remaining)?;
|
||||||
let source = get_consumed(input, remaining);
|
let source = get_consumed(input, remaining);
|
||||||
|
|
||||||
let (use_brackets, body) = match body {
|
let (use_brackets, contents, body) = match body {
|
||||||
ScriptBody::Braceless(text) => (false, vec![Object::PlainText(PlainText { source: text })]),
|
ScriptBody::Braceless(text) => (
|
||||||
ScriptBody::WithBraces(body) => (true, body),
|
false,
|
||||||
|
text,
|
||||||
|
vec![Object::PlainText(PlainText { source: text })],
|
||||||
|
),
|
||||||
|
ScriptBody::WithBraces(contents, body) => (true, contents, body),
|
||||||
};
|
};
|
||||||
|
|
||||||
Ok((
|
Ok((
|
||||||
@@ -103,6 +113,8 @@ pub(crate) fn superscript<'b, 'g, 'r, 's>(
|
|||||||
Superscript {
|
Superscript {
|
||||||
source: source.into(),
|
source: source.into(),
|
||||||
use_brackets,
|
use_brackets,
|
||||||
|
contents,
|
||||||
|
post_blank: post_blank.map(Into::<&str>::into),
|
||||||
children: body,
|
children: body,
|
||||||
},
|
},
|
||||||
))
|
))
|
||||||
@@ -117,7 +129,7 @@ fn pre<'s>(input: OrgSource<'s>) -> Res<OrgSource<'s>, ()> {
|
|||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
enum ScriptBody<'s> {
|
enum ScriptBody<'s> {
|
||||||
Braceless(&'s str),
|
Braceless(&'s str),
|
||||||
WithBraces(Vec<Object<'s>>),
|
WithBraces(&'s str, Vec<Object<'s>>),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg_attr(
|
#[cfg_attr(
|
||||||
@@ -135,9 +147,10 @@ fn script_body<'b, 'g, 'r, 's>(
|
|||||||
map(parser_with_context!(script_alphanum)(context), |body| {
|
map(parser_with_context!(script_alphanum)(context), |body| {
|
||||||
ScriptBody::Braceless(body.into())
|
ScriptBody::Braceless(body.into())
|
||||||
}),
|
}),
|
||||||
map(parser_with_context!(script_with_braces)(context), |body| {
|
map(
|
||||||
ScriptBody::WithBraces(body)
|
parser_with_context!(script_with_braces)(context),
|
||||||
}),
|
|(contents, body)| ScriptBody::WithBraces(Into::<&str>::into(contents), body),
|
||||||
|
),
|
||||||
map(
|
map(
|
||||||
parser_with_context!(script_with_parenthesis)(context),
|
parser_with_context!(script_with_parenthesis)(context),
|
||||||
|body| ScriptBody::Braceless(body.into()),
|
|body| ScriptBody::Braceless(body.into()),
|
||||||
@@ -195,7 +208,7 @@ fn end_script_alphanum_character<'s>(input: OrgSource<'s>) -> Res<OrgSource<'s>,
|
|||||||
fn script_with_braces<'b, 'g, 'r, 's>(
|
fn script_with_braces<'b, 'g, 'r, 's>(
|
||||||
context: RefContext<'b, 'g, 'r, 's>,
|
context: RefContext<'b, 'g, 'r, 's>,
|
||||||
input: OrgSource<'s>,
|
input: OrgSource<'s>,
|
||||||
) -> Res<OrgSource<'s>, Vec<Object<'s>>> {
|
) -> Res<OrgSource<'s>, (OrgSource<'s>, Vec<Object<'s>>)> {
|
||||||
let (remaining, _) = tag("{")(input)?;
|
let (remaining, _) = tag("{")(input)?;
|
||||||
let exit_with_depth = script_with_braces_end(remaining.get_brace_depth());
|
let exit_with_depth = script_with_braces_end(remaining.get_brace_depth());
|
||||||
let parser_context = ContextElement::ExitMatcherNode(ExitMatcherNode {
|
let parser_context = ContextElement::ExitMatcherNode(ExitMatcherNode {
|
||||||
@@ -204,13 +217,13 @@ fn script_with_braces<'b, 'g, 'r, 's>(
|
|||||||
});
|
});
|
||||||
let parser_context = context.with_additional_node(&parser_context);
|
let parser_context = context.with_additional_node(&parser_context);
|
||||||
|
|
||||||
let (remaining, (children, _exit_contents)) = many_till(
|
let (remaining, (contents, (children, _exit_contents))) = consumed(many_till(
|
||||||
parser_with_context!(standard_set_object)(&parser_context),
|
parser_with_context!(standard_set_object)(&parser_context),
|
||||||
parser_with_context!(exit_matcher_parser)(&parser_context),
|
parser_with_context!(exit_matcher_parser)(&parser_context),
|
||||||
)(remaining)?;
|
))(remaining)?;
|
||||||
|
|
||||||
let (remaining, _) = tag("}")(remaining)?;
|
let (remaining, _) = tag("}")(remaining)?;
|
||||||
Ok((remaining, children))
|
Ok((remaining, (contents, children)))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn script_with_braces_end(starting_brace_depth: BracketDepth) -> impl ContextMatcher {
|
fn script_with_braces_end(starting_brace_depth: BracketDepth) -> impl ContextMatcher {
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ use nom::bytes::complete::is_not;
|
|||||||
use nom::bytes::complete::tag;
|
use nom::bytes::complete::tag;
|
||||||
use nom::character::complete::line_ending;
|
use nom::character::complete::line_ending;
|
||||||
use nom::character::complete::space0;
|
use nom::character::complete::space0;
|
||||||
|
use nom::combinator::consumed;
|
||||||
use nom::combinator::not;
|
use nom::combinator::not;
|
||||||
use nom::combinator::opt;
|
use nom::combinator::opt;
|
||||||
use nom::combinator::peek;
|
use nom::combinator::peek;
|
||||||
@@ -67,13 +68,13 @@ where
|
|||||||
let org_mode_table_row_matcher = parser_with_context!(org_mode_table_row)(&parser_context);
|
let org_mode_table_row_matcher = parser_with_context!(org_mode_table_row)(&parser_context);
|
||||||
let exit_matcher = parser_with_context!(exit_matcher_parser)(&parser_context);
|
let exit_matcher = parser_with_context!(exit_matcher_parser)(&parser_context);
|
||||||
|
|
||||||
let (remaining, (children, _exit_contents)) =
|
let (remaining, (contents, (children, _exit_contents))) =
|
||||||
many_till(org_mode_table_row_matcher, exit_matcher)(remaining)?;
|
consumed(many_till(org_mode_table_row_matcher, exit_matcher))(remaining)?;
|
||||||
|
|
||||||
let (remaining, formulas) =
|
let (remaining, formulas) =
|
||||||
many0(parser_with_context!(table_formula_keyword)(context))(remaining)?;
|
many0(parser_with_context!(table_formula_keyword)(context))(remaining)?;
|
||||||
|
|
||||||
let (remaining, _trailing_ws) =
|
let (remaining, post_blank) =
|
||||||
maybe_consume_trailing_whitespace_if_not_exiting(context, remaining)?;
|
maybe_consume_trailing_whitespace_if_not_exiting(context, remaining)?;
|
||||||
let source = get_consumed(input, remaining);
|
let source = get_consumed(input, remaining);
|
||||||
|
|
||||||
@@ -87,6 +88,8 @@ where
|
|||||||
),
|
),
|
||||||
formulas,
|
formulas,
|
||||||
children,
|
children,
|
||||||
|
contents: Into::<&str>::into(contents),
|
||||||
|
post_blank: post_blank.map(Into::<&str>::into),
|
||||||
},
|
},
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
@@ -150,6 +153,7 @@ fn org_mode_table_row_rule<'b, 'g, 'r, 's>(
|
|||||||
TableRow {
|
TableRow {
|
||||||
source: source.into(),
|
source: source.into(),
|
||||||
children: Vec::new(),
|
children: Vec::new(),
|
||||||
|
contents: None,
|
||||||
},
|
},
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
@@ -164,8 +168,8 @@ fn org_mode_table_row_regular<'b, 'g, 'r, 's>(
|
|||||||
) -> Res<OrgSource<'s>, TableRow<'s>> {
|
) -> Res<OrgSource<'s>, TableRow<'s>> {
|
||||||
start_of_line(input)?;
|
start_of_line(input)?;
|
||||||
let (remaining, _) = tuple((space0, tag("|")))(input)?;
|
let (remaining, _) = tuple((space0, tag("|")))(input)?;
|
||||||
let (remaining, children) =
|
let (remaining, (contents, children)) =
|
||||||
many1(parser_with_context!(org_mode_table_cell)(context))(remaining)?;
|
consumed(many1(parser_with_context!(org_mode_table_cell)(context)))(remaining)?;
|
||||||
let (remaining, _tail) = recognize(tuple((space0, org_line_ending)))(remaining)?;
|
let (remaining, _tail) = recognize(tuple((space0, org_line_ending)))(remaining)?;
|
||||||
let source = get_consumed(input, remaining);
|
let source = get_consumed(input, remaining);
|
||||||
Ok((
|
Ok((
|
||||||
@@ -173,6 +177,11 @@ fn org_mode_table_row_regular<'b, 'g, 'r, 's>(
|
|||||||
TableRow {
|
TableRow {
|
||||||
source: source.into(),
|
source: source.into(),
|
||||||
children,
|
children,
|
||||||
|
contents: if contents.len() > 0 {
|
||||||
|
Some(Into::<&str>::into(contents))
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
},
|
||||||
},
|
},
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
@@ -194,12 +203,12 @@ fn org_mode_table_cell<'b, 'g, 'r, 's>(
|
|||||||
parser_with_context!(table_cell_set_object)(&parser_context);
|
parser_with_context!(table_cell_set_object)(&parser_context);
|
||||||
let exit_matcher = parser_with_context!(exit_matcher_parser)(&parser_context);
|
let exit_matcher = parser_with_context!(exit_matcher_parser)(&parser_context);
|
||||||
let (remaining, _) = space0(input)?;
|
let (remaining, _) = space0(input)?;
|
||||||
let (remaining, (children, _exit_contents)) = verify(
|
let (remaining, (contents, (children, _exit_contents))) = consumed(verify(
|
||||||
many_till(table_cell_set_object_matcher, exit_matcher),
|
many_till(table_cell_set_object_matcher, exit_matcher),
|
||||||
|(children, exit_contents)| {
|
|(children, exit_contents)| {
|
||||||
!children.is_empty() || Into::<&str>::into(exit_contents).ends_with('|')
|
!children.is_empty() || Into::<&str>::into(exit_contents).ends_with('|')
|
||||||
},
|
},
|
||||||
)(remaining)?;
|
))(remaining)?;
|
||||||
|
|
||||||
let (remaining, _tail) = org_mode_table_cell_end(&parser_context, remaining)?;
|
let (remaining, _tail) = org_mode_table_cell_end(&parser_context, remaining)?;
|
||||||
|
|
||||||
@@ -210,6 +219,7 @@ fn org_mode_table_cell<'b, 'g, 'r, 's>(
|
|||||||
TableCell {
|
TableCell {
|
||||||
source: source.into(),
|
source: source.into(),
|
||||||
children,
|
children,
|
||||||
|
contents: Into::<&str>::into(contents),
|
||||||
},
|
},
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -46,7 +46,7 @@ pub(crate) fn target<'b, 'g, 'r, 's>(
|
|||||||
)));
|
)));
|
||||||
}
|
}
|
||||||
let (remaining, _) = tag(">>")(remaining)?;
|
let (remaining, _) = tag(">>")(remaining)?;
|
||||||
let (remaining, _trailing_whitespace) =
|
let (remaining, post_blank) =
|
||||||
maybe_consume_object_trailing_whitespace_if_not_exiting(context, remaining)?;
|
maybe_consume_object_trailing_whitespace_if_not_exiting(context, remaining)?;
|
||||||
let source = get_consumed(input, remaining);
|
let source = get_consumed(input, remaining);
|
||||||
|
|
||||||
@@ -55,6 +55,7 @@ pub(crate) fn target<'b, 'g, 'r, 's>(
|
|||||||
Target {
|
Target {
|
||||||
source: source.into(),
|
source: source.into(),
|
||||||
value: body.into(),
|
value: body.into(),
|
||||||
|
post_blank: post_blank.map(Into::<&str>::into),
|
||||||
},
|
},
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,11 +3,13 @@ use nom::bytes::complete::tag;
|
|||||||
use nom::character::complete::anychar;
|
use nom::character::complete::anychar;
|
||||||
use nom::character::complete::multispace1;
|
use nom::character::complete::multispace1;
|
||||||
use nom::character::complete::one_of;
|
use nom::character::complete::one_of;
|
||||||
use nom::character::complete::space0;
|
use nom::character::complete::space1;
|
||||||
use nom::combinator::all_consuming;
|
use nom::combinator::all_consuming;
|
||||||
|
use nom::combinator::consumed;
|
||||||
use nom::combinator::map;
|
use nom::combinator::map;
|
||||||
use nom::combinator::map_parser;
|
use nom::combinator::map_parser;
|
||||||
use nom::combinator::not;
|
use nom::combinator::not;
|
||||||
|
use nom::combinator::opt;
|
||||||
use nom::combinator::peek;
|
use nom::combinator::peek;
|
||||||
use nom::combinator::recognize;
|
use nom::combinator::recognize;
|
||||||
use nom::combinator::verify;
|
use nom::combinator::verify;
|
||||||
@@ -76,12 +78,14 @@ fn bold<'b, 'g, 'r, 's>(
|
|||||||
context: RefContext<'b, 'g, 'r, 's>,
|
context: RefContext<'b, 'g, 'r, 's>,
|
||||||
input: OrgSource<'s>,
|
input: OrgSource<'s>,
|
||||||
) -> Res<OrgSource<'s>, Bold<'s>> {
|
) -> Res<OrgSource<'s>, Bold<'s>> {
|
||||||
let (remaining, children) = text_markup_object("*")(context, input)?;
|
let (remaining, (contents, children, post_blank)) = text_markup_object("*")(context, input)?;
|
||||||
let source = get_consumed(input, remaining);
|
let source = get_consumed(input, remaining);
|
||||||
Ok((
|
Ok((
|
||||||
remaining,
|
remaining,
|
||||||
Bold {
|
Bold {
|
||||||
source: source.into(),
|
source: source.into(),
|
||||||
|
contents: contents.into(),
|
||||||
|
post_blank: post_blank.map(Into::<&str>::into),
|
||||||
children,
|
children,
|
||||||
},
|
},
|
||||||
))
|
))
|
||||||
@@ -95,12 +99,14 @@ fn italic<'b, 'g, 'r, 's>(
|
|||||||
context: RefContext<'b, 'g, 'r, 's>,
|
context: RefContext<'b, 'g, 'r, 's>,
|
||||||
input: OrgSource<'s>,
|
input: OrgSource<'s>,
|
||||||
) -> Res<OrgSource<'s>, Italic<'s>> {
|
) -> Res<OrgSource<'s>, Italic<'s>> {
|
||||||
let (remaining, children) = text_markup_object("/")(context, input)?;
|
let (remaining, (contents, children, post_blank)) = text_markup_object("/")(context, input)?;
|
||||||
let source = get_consumed(input, remaining);
|
let source = get_consumed(input, remaining);
|
||||||
Ok((
|
Ok((
|
||||||
remaining,
|
remaining,
|
||||||
Italic {
|
Italic {
|
||||||
source: source.into(),
|
source: source.into(),
|
||||||
|
contents: contents.into(),
|
||||||
|
post_blank: post_blank.map(Into::<&str>::into),
|
||||||
children,
|
children,
|
||||||
},
|
},
|
||||||
))
|
))
|
||||||
@@ -114,12 +120,14 @@ fn underline<'b, 'g, 'r, 's>(
|
|||||||
context: RefContext<'b, 'g, 'r, 's>,
|
context: RefContext<'b, 'g, 'r, 's>,
|
||||||
input: OrgSource<'s>,
|
input: OrgSource<'s>,
|
||||||
) -> Res<OrgSource<'s>, Underline<'s>> {
|
) -> Res<OrgSource<'s>, Underline<'s>> {
|
||||||
let (remaining, children) = text_markup_object("_")(context, input)?;
|
let (remaining, (contents, children, post_blank)) = text_markup_object("_")(context, input)?;
|
||||||
let source = get_consumed(input, remaining);
|
let source = get_consumed(input, remaining);
|
||||||
Ok((
|
Ok((
|
||||||
remaining,
|
remaining,
|
||||||
Underline {
|
Underline {
|
||||||
source: source.into(),
|
source: source.into(),
|
||||||
|
contents: contents.into(),
|
||||||
|
post_blank: post_blank.map(Into::<&str>::into),
|
||||||
children,
|
children,
|
||||||
},
|
},
|
||||||
))
|
))
|
||||||
@@ -133,12 +141,14 @@ fn strike_through<'b, 'g, 'r, 's>(
|
|||||||
context: RefContext<'b, 'g, 'r, 's>,
|
context: RefContext<'b, 'g, 'r, 's>,
|
||||||
input: OrgSource<'s>,
|
input: OrgSource<'s>,
|
||||||
) -> Res<OrgSource<'s>, StrikeThrough<'s>> {
|
) -> Res<OrgSource<'s>, StrikeThrough<'s>> {
|
||||||
let (remaining, children) = text_markup_object("+")(context, input)?;
|
let (remaining, (contents, children, post_blank)) = text_markup_object("+")(context, input)?;
|
||||||
let source = get_consumed(input, remaining);
|
let source = get_consumed(input, remaining);
|
||||||
Ok((
|
Ok((
|
||||||
remaining,
|
remaining,
|
||||||
StrikeThrough {
|
StrikeThrough {
|
||||||
source: source.into(),
|
source: source.into(),
|
||||||
|
contents: contents.into(),
|
||||||
|
post_blank: post_blank.map(Into::<&str>::into),
|
||||||
children,
|
children,
|
||||||
},
|
},
|
||||||
))
|
))
|
||||||
@@ -152,13 +162,14 @@ fn verbatim<'b, 'g, 'r, 's>(
|
|||||||
context: RefContext<'b, 'g, 'r, 's>,
|
context: RefContext<'b, 'g, 'r, 's>,
|
||||||
input: OrgSource<'s>,
|
input: OrgSource<'s>,
|
||||||
) -> Res<OrgSource<'s>, Verbatim<'s>> {
|
) -> Res<OrgSource<'s>, Verbatim<'s>> {
|
||||||
let (remaining, contents) = text_markup_string("=")(context, input)?;
|
let (remaining, (contents, post_blank)) = text_markup_string("=")(context, input)?;
|
||||||
let source = get_consumed(input, remaining);
|
let source = get_consumed(input, remaining);
|
||||||
Ok((
|
Ok((
|
||||||
remaining,
|
remaining,
|
||||||
Verbatim {
|
Verbatim {
|
||||||
source: source.into(),
|
source: source.into(),
|
||||||
contents: contents.into(),
|
contents: contents.into(),
|
||||||
|
post_blank: post_blank.map(Into::<&str>::into),
|
||||||
},
|
},
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
@@ -171,13 +182,14 @@ fn code<'b, 'g, 'r, 's>(
|
|||||||
context: RefContext<'b, 'g, 'r, 's>,
|
context: RefContext<'b, 'g, 'r, 's>,
|
||||||
input: OrgSource<'s>,
|
input: OrgSource<'s>,
|
||||||
) -> Res<OrgSource<'s>, Code<'s>> {
|
) -> Res<OrgSource<'s>, Code<'s>> {
|
||||||
let (remaining, contents) = text_markup_string("~")(context, input)?;
|
let (remaining, (contents, post_blank)) = text_markup_string("~")(context, input)?;
|
||||||
let source = get_consumed(input, remaining);
|
let source = get_consumed(input, remaining);
|
||||||
Ok((
|
Ok((
|
||||||
remaining,
|
remaining,
|
||||||
Code {
|
Code {
|
||||||
source: source.into(),
|
source: source.into(),
|
||||||
contents: contents.into(),
|
contents: contents.into(),
|
||||||
|
post_blank: post_blank.map(Into::<&str>::into),
|
||||||
},
|
},
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
@@ -187,8 +199,10 @@ fn text_markup_object(
|
|||||||
) -> impl for<'b, 'g, 'r, 's> Fn(
|
) -> impl for<'b, 'g, 'r, 's> Fn(
|
||||||
RefContext<'b, 'g, 'r, 's>,
|
RefContext<'b, 'g, 'r, 's>,
|
||||||
OrgSource<'s>,
|
OrgSource<'s>,
|
||||||
) -> Res<OrgSource<'s>, Vec<Object<'s>>>
|
) -> Res<
|
||||||
+ '_ {
|
OrgSource<'s>,
|
||||||
|
(OrgSource<'s>, Vec<Object<'s>>, Option<OrgSource<'s>>),
|
||||||
|
> + '_ {
|
||||||
move |context, input: OrgSource<'_>| _text_markup_object(context, input, marker_symbol)
|
move |context, input: OrgSource<'_>| _text_markup_object(context, input, marker_symbol)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -200,7 +214,7 @@ fn _text_markup_object<'b, 'g, 'r, 's, 'c>(
|
|||||||
context: RefContext<'b, 'g, 'r, 's>,
|
context: RefContext<'b, 'g, 'r, 's>,
|
||||||
input: OrgSource<'s>,
|
input: OrgSource<'s>,
|
||||||
marker_symbol: &'c str,
|
marker_symbol: &'c str,
|
||||||
) -> Res<OrgSource<'s>, Vec<Object<'s>>> {
|
) -> Res<OrgSource<'s>, (OrgSource<'s>, Vec<Object<'s>>, Option<OrgSource<'s>>)> {
|
||||||
let (remaining, _) = pre(context, input)?;
|
let (remaining, _) = pre(context, input)?;
|
||||||
let (remaining, open) = tag(marker_symbol)(remaining)?;
|
let (remaining, open) = tag(marker_symbol)(remaining)?;
|
||||||
let (remaining, _peek_not_whitespace) =
|
let (remaining, _peek_not_whitespace) =
|
||||||
@@ -215,7 +229,7 @@ fn _text_markup_object<'b, 'g, 'r, 's, 'c>(
|
|||||||
let initial_context = ContextElement::document_context();
|
let initial_context = ContextElement::document_context();
|
||||||
let initial_context = Context::new(context.get_global_settings(), List::new(&initial_context));
|
let initial_context = Context::new(context.get_global_settings(), List::new(&initial_context));
|
||||||
|
|
||||||
let (remaining, children) = map_parser(
|
let (remaining, (contents, children)) = consumed(map_parser(
|
||||||
verify(
|
verify(
|
||||||
parser_with_context!(text_until_exit)(&parser_context),
|
parser_with_context!(text_until_exit)(&parser_context),
|
||||||
|text| text.len() > 0,
|
|text| text.len() > 0,
|
||||||
@@ -225,7 +239,7 @@ fn _text_markup_object<'b, 'g, 'r, 's, 'c>(
|
|||||||
&initial_context,
|
&initial_context,
|
||||||
)))(i)
|
)))(i)
|
||||||
}),
|
}),
|
||||||
)(remaining)?;
|
))(remaining)?;
|
||||||
|
|
||||||
{
|
{
|
||||||
#[cfg(feature = "tracing")]
|
#[cfg(feature = "tracing")]
|
||||||
@@ -240,9 +254,9 @@ fn _text_markup_object<'b, 'g, 'r, 's, 'c>(
|
|||||||
}
|
}
|
||||||
|
|
||||||
let (remaining, _close) = text_markup_end_specialized(context, remaining)?;
|
let (remaining, _close) = text_markup_end_specialized(context, remaining)?;
|
||||||
let (remaining, _trailing_whitespace) =
|
let (remaining, post_blank) =
|
||||||
maybe_consume_object_trailing_whitespace_if_not_exiting(context, remaining)?;
|
maybe_consume_object_trailing_whitespace_if_not_exiting(context, remaining)?;
|
||||||
Ok((remaining, children))
|
Ok((remaining, (contents, children, post_blank)))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn text_markup_string(
|
fn text_markup_string(
|
||||||
@@ -250,7 +264,7 @@ fn text_markup_string(
|
|||||||
) -> impl for<'b, 'g, 'r, 's> Fn(
|
) -> impl for<'b, 'g, 'r, 's> Fn(
|
||||||
RefContext<'b, 'g, 'r, 's>,
|
RefContext<'b, 'g, 'r, 's>,
|
||||||
OrgSource<'s>,
|
OrgSource<'s>,
|
||||||
) -> Res<OrgSource<'s>, OrgSource<'s>>
|
) -> Res<OrgSource<'s>, (OrgSource<'s>, Option<OrgSource<'s>>)>
|
||||||
+ '_ {
|
+ '_ {
|
||||||
move |context, input: OrgSource<'_>| _text_markup_string(context, input, marker_symbol)
|
move |context, input: OrgSource<'_>| _text_markup_string(context, input, marker_symbol)
|
||||||
}
|
}
|
||||||
@@ -263,7 +277,7 @@ fn _text_markup_string<'b, 'g, 'r, 's, 'c>(
|
|||||||
context: RefContext<'b, 'g, 'r, 's>,
|
context: RefContext<'b, 'g, 'r, 's>,
|
||||||
input: OrgSource<'s>,
|
input: OrgSource<'s>,
|
||||||
marker_symbol: &'c str,
|
marker_symbol: &'c str,
|
||||||
) -> Res<OrgSource<'s>, OrgSource<'s>> {
|
) -> Res<OrgSource<'s>, (OrgSource<'s>, Option<OrgSource<'s>>)> {
|
||||||
let (remaining, _) = pre(context, input)?;
|
let (remaining, _) = pre(context, input)?;
|
||||||
let (remaining, open) = tag(marker_symbol)(remaining)?;
|
let (remaining, open) = tag(marker_symbol)(remaining)?;
|
||||||
let (remaining, _peek_not_whitespace) =
|
let (remaining, _peek_not_whitespace) =
|
||||||
@@ -296,9 +310,9 @@ fn _text_markup_string<'b, 'g, 'r, 's, 'c>(
|
|||||||
}
|
}
|
||||||
|
|
||||||
let (remaining, _close) = text_markup_end_specialized(context, remaining)?;
|
let (remaining, _close) = text_markup_end_specialized(context, remaining)?;
|
||||||
let (remaining, _trailing_whitespace) =
|
let (remaining, post_blank) =
|
||||||
maybe_consume_object_trailing_whitespace_if_not_exiting(context, remaining)?;
|
maybe_consume_object_trailing_whitespace_if_not_exiting(context, remaining)?;
|
||||||
Ok((remaining, contents))
|
Ok((remaining, (contents, post_blank)))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg_attr(
|
#[cfg_attr(
|
||||||
@@ -382,13 +396,15 @@ impl<'x> RematchObject<'x> for Bold<'x> {
|
|||||||
_context: RefContext<'b, 'g, 'r, 's>,
|
_context: RefContext<'b, 'g, 'r, 's>,
|
||||||
input: OrgSource<'s>,
|
input: OrgSource<'s>,
|
||||||
) -> Res<OrgSource<'s>, Object<'s>> {
|
) -> Res<OrgSource<'s>, Object<'s>> {
|
||||||
let (remaining, children) =
|
let (remaining, (contents, children, post_blank)) =
|
||||||
_rematch_text_markup_object(_context, input, "*", &self.children)?;
|
_rematch_text_markup_object(_context, input, "*", &self.children)?;
|
||||||
let source = get_consumed(input, remaining);
|
let source = get_consumed(input, remaining);
|
||||||
Ok((
|
Ok((
|
||||||
remaining,
|
remaining,
|
||||||
Object::Bold(Bold {
|
Object::Bold(Bold {
|
||||||
source: source.into(),
|
source: source.into(),
|
||||||
|
contents: contents.into(),
|
||||||
|
post_blank: post_blank.map(Into::<&str>::into),
|
||||||
children,
|
children,
|
||||||
}),
|
}),
|
||||||
))
|
))
|
||||||
@@ -405,13 +421,15 @@ impl<'x> RematchObject<'x> for Italic<'x> {
|
|||||||
_context: RefContext<'b, 'g, 'r, 's>,
|
_context: RefContext<'b, 'g, 'r, 's>,
|
||||||
input: OrgSource<'s>,
|
input: OrgSource<'s>,
|
||||||
) -> Res<OrgSource<'s>, Object<'s>> {
|
) -> Res<OrgSource<'s>, Object<'s>> {
|
||||||
let (remaining, children) =
|
let (remaining, (contents, children, post_blank)) =
|
||||||
_rematch_text_markup_object(_context, input, "/", &self.children)?;
|
_rematch_text_markup_object(_context, input, "/", &self.children)?;
|
||||||
let source = get_consumed(input, remaining);
|
let source = get_consumed(input, remaining);
|
||||||
Ok((
|
Ok((
|
||||||
remaining,
|
remaining,
|
||||||
Object::Italic(Italic {
|
Object::Italic(Italic {
|
||||||
source: source.into(),
|
source: source.into(),
|
||||||
|
contents: contents.into(),
|
||||||
|
post_blank: post_blank.map(Into::<&str>::into),
|
||||||
children,
|
children,
|
||||||
}),
|
}),
|
||||||
))
|
))
|
||||||
@@ -428,13 +446,15 @@ impl<'x> RematchObject<'x> for Underline<'x> {
|
|||||||
_context: RefContext<'b, 'g, 'r, 's>,
|
_context: RefContext<'b, 'g, 'r, 's>,
|
||||||
input: OrgSource<'s>,
|
input: OrgSource<'s>,
|
||||||
) -> Res<OrgSource<'s>, Object<'s>> {
|
) -> Res<OrgSource<'s>, Object<'s>> {
|
||||||
let (remaining, children) =
|
let (remaining, (contents, children, post_blank)) =
|
||||||
_rematch_text_markup_object(_context, input, "_", &self.children)?;
|
_rematch_text_markup_object(_context, input, "_", &self.children)?;
|
||||||
let source = get_consumed(input, remaining);
|
let source = get_consumed(input, remaining);
|
||||||
Ok((
|
Ok((
|
||||||
remaining,
|
remaining,
|
||||||
Object::Underline(Underline {
|
Object::Underline(Underline {
|
||||||
source: source.into(),
|
source: source.into(),
|
||||||
|
contents: contents.into(),
|
||||||
|
post_blank: post_blank.map(Into::<&str>::into),
|
||||||
children,
|
children,
|
||||||
}),
|
}),
|
||||||
))
|
))
|
||||||
@@ -451,13 +471,15 @@ impl<'x> RematchObject<'x> for StrikeThrough<'x> {
|
|||||||
_context: RefContext<'b, 'g, 'r, 's>,
|
_context: RefContext<'b, 'g, 'r, 's>,
|
||||||
input: OrgSource<'s>,
|
input: OrgSource<'s>,
|
||||||
) -> Res<OrgSource<'s>, Object<'s>> {
|
) -> Res<OrgSource<'s>, Object<'s>> {
|
||||||
let (remaining, children) =
|
let (remaining, (contents, children, post_blank)) =
|
||||||
_rematch_text_markup_object(_context, input, "+", &self.children)?;
|
_rematch_text_markup_object(_context, input, "+", &self.children)?;
|
||||||
let source = get_consumed(input, remaining);
|
let source = get_consumed(input, remaining);
|
||||||
Ok((
|
Ok((
|
||||||
remaining,
|
remaining,
|
||||||
Object::StrikeThrough(StrikeThrough {
|
Object::StrikeThrough(StrikeThrough {
|
||||||
source: source.into(),
|
source: source.into(),
|
||||||
|
contents: contents.into(),
|
||||||
|
post_blank: post_blank.map(Into::<&str>::into),
|
||||||
children,
|
children,
|
||||||
}),
|
}),
|
||||||
))
|
))
|
||||||
@@ -473,7 +495,7 @@ fn _rematch_text_markup_object<'b, 'g, 'r, 's, 'x>(
|
|||||||
input: OrgSource<'s>,
|
input: OrgSource<'s>,
|
||||||
marker_symbol: &'static str,
|
marker_symbol: &'static str,
|
||||||
original_match_children: &'x Vec<Object<'x>>,
|
original_match_children: &'x Vec<Object<'x>>,
|
||||||
) -> Res<OrgSource<'s>, Vec<Object<'s>>> {
|
) -> Res<OrgSource<'s>, (OrgSource<'s>, Vec<Object<'s>>, Option<OrgSource<'s>>)> {
|
||||||
let (remaining, _) = pre(context, input)?;
|
let (remaining, _) = pre(context, input)?;
|
||||||
let (remaining, open) = tag(marker_symbol)(remaining)?;
|
let (remaining, open) = tag(marker_symbol)(remaining)?;
|
||||||
let (remaining, _peek_not_whitespace) = peek(not(multispace1))(remaining)?;
|
let (remaining, _peek_not_whitespace) = peek(not(multispace1))(remaining)?;
|
||||||
@@ -484,6 +506,7 @@ fn _rematch_text_markup_object<'b, 'g, 'r, 's, 'x>(
|
|||||||
});
|
});
|
||||||
let parser_context = context.with_additional_node(&parser_context);
|
let parser_context = context.with_additional_node(&parser_context);
|
||||||
|
|
||||||
|
let contents_begin = remaining;
|
||||||
let (remaining, children) =
|
let (remaining, children) =
|
||||||
// TODO: This doesn't really check the exit matcher between each object. I think it may be possible to construct an org document that parses incorrectly with the current code.
|
// TODO: This doesn't really check the exit matcher between each object. I think it may be possible to construct an org document that parses incorrectly with the current code.
|
||||||
rematch_target(&parser_context, original_match_children, remaining)?;
|
rematch_target(&parser_context, original_match_children, remaining)?;
|
||||||
@@ -499,8 +522,10 @@ fn _rematch_text_markup_object<'b, 'g, 'r, 's, 'x>(
|
|||||||
)));
|
)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
let contents_end = remaining;
|
||||||
|
let contents = contents_begin.get_until(contents_end);
|
||||||
|
|
||||||
let (remaining, _close) = text_markup_end_specialized(context, remaining)?;
|
let (remaining, _close) = text_markup_end_specialized(context, remaining)?;
|
||||||
let (remaining, _trailing_whitespace) = space0(remaining)?;
|
let (remaining, post_blank) = opt(space1)(remaining)?;
|
||||||
Ok((remaining, children))
|
Ok((remaining, (contents, children, post_blank)))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -53,8 +53,8 @@ pub(crate) fn timestamp<'b, 'g, 'r, 's>(
|
|||||||
parser_with_context!(inactive_time_range_timestamp)(context),
|
parser_with_context!(inactive_time_range_timestamp)(context),
|
||||||
parser_with_context!(active_date_range_timestamp)(context),
|
parser_with_context!(active_date_range_timestamp)(context),
|
||||||
parser_with_context!(inactive_date_range_timestamp)(context),
|
parser_with_context!(inactive_date_range_timestamp)(context),
|
||||||
parser_with_context!(active_timestamp)(context),
|
parser_with_context!(active_timestamp(true))(context),
|
||||||
parser_with_context!(inactive_timestamp)(context),
|
parser_with_context!(inactive_timestamp(true))(context),
|
||||||
))(input)
|
))(input)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -69,7 +69,7 @@ fn diary_timestamp<'b, 'g, 'r, 's>(
|
|||||||
let (remaining, _) = tag("<%%(")(input)?;
|
let (remaining, _) = tag("<%%(")(input)?;
|
||||||
let (remaining, _body) = sexp(context, remaining)?;
|
let (remaining, _body) = sexp(context, remaining)?;
|
||||||
let (remaining, _) = tag(")>")(remaining)?;
|
let (remaining, _) = tag(")>")(remaining)?;
|
||||||
let (remaining, _trailing_whitespace) =
|
let (remaining, post_blank) =
|
||||||
maybe_consume_object_trailing_whitespace_if_not_exiting(context, remaining)?;
|
maybe_consume_object_trailing_whitespace_if_not_exiting(context, remaining)?;
|
||||||
let source = get_consumed(input, remaining);
|
let source = get_consumed(input, remaining);
|
||||||
|
|
||||||
@@ -85,6 +85,7 @@ fn diary_timestamp<'b, 'g, 'r, 's>(
|
|||||||
end_time: None,
|
end_time: None,
|
||||||
repeater: None,
|
repeater: None,
|
||||||
warning_delay: None,
|
warning_delay: None,
|
||||||
|
post_blank: post_blank.map(Into::<&str>::into),
|
||||||
},
|
},
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
@@ -125,13 +126,23 @@ fn sexp_end<'b, 'g, 'r, 's>(
|
|||||||
alt((tag(")>"), recognize(one_of(">\n"))))(input)
|
alt((tag(")>"), recognize(one_of(">\n"))))(input)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const fn active_timestamp(
|
||||||
|
allow_post_blank: bool,
|
||||||
|
) -> impl for<'b, 'g, 'r, 's> Fn(
|
||||||
|
RefContext<'b, 'g, 'r, 's>,
|
||||||
|
OrgSource<'s>,
|
||||||
|
) -> Res<OrgSource<'s>, Timestamp<'s>> {
|
||||||
|
move |context, input| impl_active_timestamp(context, input, allow_post_blank)
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg_attr(
|
#[cfg_attr(
|
||||||
feature = "tracing",
|
feature = "tracing",
|
||||||
tracing::instrument(ret, level = "debug", skip(context))
|
tracing::instrument(ret, level = "debug", skip(context))
|
||||||
)]
|
)]
|
||||||
fn active_timestamp<'b, 'g, 'r, 's>(
|
fn impl_active_timestamp<'b, 'g, 'r, 's>(
|
||||||
context: RefContext<'b, 'g, 'r, 's>,
|
context: RefContext<'b, 'g, 'r, 's>,
|
||||||
input: OrgSource<'s>,
|
input: OrgSource<'s>,
|
||||||
|
allow_post_blank: bool,
|
||||||
) -> Res<OrgSource<'s>, Timestamp<'s>> {
|
) -> Res<OrgSource<'s>, Timestamp<'s>> {
|
||||||
let (remaining, _) = tag("<")(input)?;
|
let (remaining, _) = tag("<")(input)?;
|
||||||
let (remaining, start) = date(context, remaining)?;
|
let (remaining, start) = date(context, remaining)?;
|
||||||
@@ -159,8 +170,11 @@ fn active_timestamp<'b, 'g, 'r, 's>(
|
|||||||
)))(remaining)?;
|
)))(remaining)?;
|
||||||
let (remaining, _) = tag(">")(remaining)?;
|
let (remaining, _) = tag(">")(remaining)?;
|
||||||
|
|
||||||
let (remaining, _trailing_whitespace) =
|
let (remaining, post_blank) = if allow_post_blank {
|
||||||
maybe_consume_object_trailing_whitespace_if_not_exiting(context, remaining)?;
|
maybe_consume_object_trailing_whitespace_if_not_exiting(context, remaining)?
|
||||||
|
} else {
|
||||||
|
(remaining, None)
|
||||||
|
};
|
||||||
let source = get_consumed(input, remaining);
|
let source = get_consumed(input, remaining);
|
||||||
|
|
||||||
Ok((
|
Ok((
|
||||||
@@ -175,17 +189,28 @@ fn active_timestamp<'b, 'g, 'r, 's>(
|
|||||||
end_time: time.map(|(_, time)| time),
|
end_time: time.map(|(_, time)| time),
|
||||||
repeater: repeater.map(|(_, repeater)| repeater),
|
repeater: repeater.map(|(_, repeater)| repeater),
|
||||||
warning_delay: warning_delay.map(|(_, warning_delay)| warning_delay),
|
warning_delay: warning_delay.map(|(_, warning_delay)| warning_delay),
|
||||||
|
post_blank: post_blank.map(Into::<&str>::into),
|
||||||
},
|
},
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(crate) const fn inactive_timestamp(
|
||||||
|
allow_post_blank: bool,
|
||||||
|
) -> impl for<'b, 'g, 'r, 's> Fn(
|
||||||
|
RefContext<'b, 'g, 'r, 's>,
|
||||||
|
OrgSource<'s>,
|
||||||
|
) -> Res<OrgSource<'s>, Timestamp<'s>> {
|
||||||
|
move |context, input| impl_inactive_timestamp(context, input, allow_post_blank)
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg_attr(
|
#[cfg_attr(
|
||||||
feature = "tracing",
|
feature = "tracing",
|
||||||
tracing::instrument(ret, level = "debug", skip(context))
|
tracing::instrument(ret, level = "debug", skip(context))
|
||||||
)]
|
)]
|
||||||
pub(crate) fn inactive_timestamp<'b, 'g, 'r, 's>(
|
fn impl_inactive_timestamp<'b, 'g, 'r, 's>(
|
||||||
context: RefContext<'b, 'g, 'r, 's>,
|
context: RefContext<'b, 'g, 'r, 's>,
|
||||||
input: OrgSource<'s>,
|
input: OrgSource<'s>,
|
||||||
|
allow_post_blank: bool,
|
||||||
) -> Res<OrgSource<'s>, Timestamp<'s>> {
|
) -> Res<OrgSource<'s>, Timestamp<'s>> {
|
||||||
let (remaining, _) = tag("[")(input)?;
|
let (remaining, _) = tag("[")(input)?;
|
||||||
let (remaining, start) = date(context, remaining)?;
|
let (remaining, start) = date(context, remaining)?;
|
||||||
@@ -213,8 +238,11 @@ pub(crate) fn inactive_timestamp<'b, 'g, 'r, 's>(
|
|||||||
)))(remaining)?;
|
)))(remaining)?;
|
||||||
let (remaining, _) = tag("]")(remaining)?;
|
let (remaining, _) = tag("]")(remaining)?;
|
||||||
|
|
||||||
let (remaining, _trailing_whitespace) =
|
let (remaining, post_blank) = if allow_post_blank {
|
||||||
maybe_consume_object_trailing_whitespace_if_not_exiting(context, remaining)?;
|
maybe_consume_object_trailing_whitespace_if_not_exiting(context, remaining)?
|
||||||
|
} else {
|
||||||
|
(remaining, None)
|
||||||
|
};
|
||||||
let source = get_consumed(input, remaining);
|
let source = get_consumed(input, remaining);
|
||||||
|
|
||||||
Ok((
|
Ok((
|
||||||
@@ -229,6 +257,7 @@ pub(crate) fn inactive_timestamp<'b, 'g, 'r, 's>(
|
|||||||
end_time: time.map(|(_, time)| time),
|
end_time: time.map(|(_, time)| time),
|
||||||
repeater: repeater.map(|(_, repeater)| repeater),
|
repeater: repeater.map(|(_, repeater)| repeater),
|
||||||
warning_delay: warning_delay.map(|(_, warning_delay)| warning_delay),
|
warning_delay: warning_delay.map(|(_, warning_delay)| warning_delay),
|
||||||
|
post_blank: post_blank.map(Into::<&str>::into),
|
||||||
},
|
},
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
@@ -241,12 +270,12 @@ fn active_date_range_timestamp<'b, 'g, 'r, 's>(
|
|||||||
context: RefContext<'b, 'g, 'r, 's>,
|
context: RefContext<'b, 'g, 'r, 's>,
|
||||||
input: OrgSource<'s>,
|
input: OrgSource<'s>,
|
||||||
) -> Res<OrgSource<'s>, Timestamp<'s>> {
|
) -> Res<OrgSource<'s>, Timestamp<'s>> {
|
||||||
let (remaining, first_timestamp) = active_timestamp(context, input)?;
|
let (remaining, first_timestamp) = impl_active_timestamp(context, input, false)?;
|
||||||
// TODO: Does the space0 at the end of the active/inactive timestamp parsers cause this to be incorrect? I could use a look-behind to make sure the preceding character is not whitespace
|
// TODO: Does the space0 at the end of the active/inactive timestamp parsers cause this to be incorrect? I could use a look-behind to make sure the preceding character is not whitespace
|
||||||
let (remaining, _separator) = tag("--")(remaining)?;
|
let (remaining, _separator) = tag("--")(remaining)?;
|
||||||
let (remaining, second_timestamp) = active_timestamp(context, remaining)?;
|
let (remaining, second_timestamp) = impl_active_timestamp(context, remaining, false)?;
|
||||||
|
|
||||||
let (remaining, _trailing_whitespace) =
|
let (remaining, post_blank) =
|
||||||
maybe_consume_object_trailing_whitespace_if_not_exiting(context, remaining)?;
|
maybe_consume_object_trailing_whitespace_if_not_exiting(context, remaining)?;
|
||||||
let source = get_consumed(input, remaining);
|
let source = get_consumed(input, remaining);
|
||||||
|
|
||||||
@@ -264,6 +293,7 @@ fn active_date_range_timestamp<'b, 'g, 'r, 's>(
|
|||||||
warning_delay: first_timestamp
|
warning_delay: first_timestamp
|
||||||
.warning_delay
|
.warning_delay
|
||||||
.or(second_timestamp.warning_delay),
|
.or(second_timestamp.warning_delay),
|
||||||
|
post_blank: post_blank.map(Into::<&str>::into),
|
||||||
},
|
},
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
@@ -302,7 +332,7 @@ fn active_time_range_timestamp<'b, 'g, 'r, 's>(
|
|||||||
)))(remaining)?;
|
)))(remaining)?;
|
||||||
let (remaining, _) = tag(">")(remaining)?;
|
let (remaining, _) = tag(">")(remaining)?;
|
||||||
|
|
||||||
let (remaining, _trailing_whitespace) =
|
let (remaining, post_blank) =
|
||||||
maybe_consume_object_trailing_whitespace_if_not_exiting(context, remaining)?;
|
maybe_consume_object_trailing_whitespace_if_not_exiting(context, remaining)?;
|
||||||
let source = get_consumed(input, remaining);
|
let source = get_consumed(input, remaining);
|
||||||
|
|
||||||
@@ -318,6 +348,7 @@ fn active_time_range_timestamp<'b, 'g, 'r, 's>(
|
|||||||
end_time: Some(second_time),
|
end_time: Some(second_time),
|
||||||
repeater: repeater.map(|(_, repeater)| repeater),
|
repeater: repeater.map(|(_, repeater)| repeater),
|
||||||
warning_delay: warning_delay.map(|(_, warning_delay)| warning_delay),
|
warning_delay: warning_delay.map(|(_, warning_delay)| warning_delay),
|
||||||
|
post_blank: post_blank.map(Into::<&str>::into),
|
||||||
},
|
},
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
@@ -330,12 +361,12 @@ pub(crate) fn inactive_date_range_timestamp<'b, 'g, 'r, 's>(
|
|||||||
context: RefContext<'b, 'g, 'r, 's>,
|
context: RefContext<'b, 'g, 'r, 's>,
|
||||||
input: OrgSource<'s>,
|
input: OrgSource<'s>,
|
||||||
) -> Res<OrgSource<'s>, Timestamp<'s>> {
|
) -> Res<OrgSource<'s>, Timestamp<'s>> {
|
||||||
let (remaining, first_timestamp) = inactive_timestamp(context, input)?;
|
let (remaining, first_timestamp) = impl_inactive_timestamp(context, input, false)?;
|
||||||
// TODO: Does the space0 at the end of the active/inactive timestamp parsers cause this to be incorrect? I could use a look-behind to make sure the preceding character is not whitespace
|
// TODO: Does the space0 at the end of the active/inactive timestamp parsers cause this to be incorrect? I could use a look-behind to make sure the preceding character is not whitespace
|
||||||
let (remaining, _separator) = tag("--")(remaining)?;
|
let (remaining, _separator) = tag("--")(remaining)?;
|
||||||
let (remaining, second_timestamp) = inactive_timestamp(context, remaining)?;
|
let (remaining, second_timestamp) = impl_inactive_timestamp(context, remaining, false)?;
|
||||||
|
|
||||||
let (remaining, _trailing_whitespace) =
|
let (remaining, post_blank) =
|
||||||
maybe_consume_object_trailing_whitespace_if_not_exiting(context, remaining)?;
|
maybe_consume_object_trailing_whitespace_if_not_exiting(context, remaining)?;
|
||||||
let source = get_consumed(input, remaining);
|
let source = get_consumed(input, remaining);
|
||||||
|
|
||||||
@@ -354,6 +385,7 @@ pub(crate) fn inactive_date_range_timestamp<'b, 'g, 'r, 's>(
|
|||||||
warning_delay: first_timestamp
|
warning_delay: first_timestamp
|
||||||
.warning_delay
|
.warning_delay
|
||||||
.or(second_timestamp.warning_delay),
|
.or(second_timestamp.warning_delay),
|
||||||
|
post_blank: post_blank.map(Into::<&str>::into),
|
||||||
},
|
},
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
@@ -392,7 +424,7 @@ pub(crate) fn inactive_time_range_timestamp<'b, 'g, 'r, 's>(
|
|||||||
)))(remaining)?;
|
)))(remaining)?;
|
||||||
let (remaining, _) = tag("]")(remaining)?;
|
let (remaining, _) = tag("]")(remaining)?;
|
||||||
|
|
||||||
let (remaining, _trailing_whitespace) =
|
let (remaining, post_blank) =
|
||||||
maybe_consume_object_trailing_whitespace_if_not_exiting(context, remaining)?;
|
maybe_consume_object_trailing_whitespace_if_not_exiting(context, remaining)?;
|
||||||
let source = get_consumed(input, remaining);
|
let source = get_consumed(input, remaining);
|
||||||
|
|
||||||
@@ -408,6 +440,7 @@ pub(crate) fn inactive_time_range_timestamp<'b, 'g, 'r, 's>(
|
|||||||
end_time: Some(second_time),
|
end_time: Some(second_time),
|
||||||
repeater: repeater.map(|(_, repeater)| repeater),
|
repeater: repeater.map(|(_, repeater)| repeater),
|
||||||
warning_delay: warning_delay.map(|(_, warning_delay)| warning_delay),
|
warning_delay: warning_delay.map(|(_, warning_delay)| warning_delay),
|
||||||
|
post_blank: post_blank.map(Into::<&str>::into),
|
||||||
},
|
},
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -81,14 +81,21 @@ pub(crate) fn maybe_consume_object_trailing_whitespace_if_not_exiting<'b, 'g, 'r
|
|||||||
input: OrgSource<'s>,
|
input: OrgSource<'s>,
|
||||||
) -> Res<OrgSource<'s>, Option<OrgSource<'s>>> {
|
) -> Res<OrgSource<'s>, Option<OrgSource<'s>>> {
|
||||||
// We have to check exit matcher after each character because description list tags need to end with a space unconsumed (" ::").
|
// We have to check exit matcher after each character because description list tags need to end with a space unconsumed (" ::").
|
||||||
let (remaining, _) = many_till(
|
let (remaining, post_blank) = recognize(many_till(
|
||||||
one_of(" \t"),
|
one_of(" \t"),
|
||||||
alt((
|
alt((
|
||||||
peek(recognize(none_of(" \t"))),
|
peek(recognize(none_of(" \t"))),
|
||||||
parser_with_context!(exit_matcher_parser)(context),
|
parser_with_context!(exit_matcher_parser)(context),
|
||||||
)),
|
)),
|
||||||
)(input)?;
|
))(input)?;
|
||||||
Ok((remaining, None))
|
Ok((
|
||||||
|
remaining,
|
||||||
|
if post_blank.len() == 0 {
|
||||||
|
None
|
||||||
|
} else {
|
||||||
|
Some(post_blank)
|
||||||
|
},
|
||||||
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg_attr(
|
#[cfg_attr(
|
||||||
|
|||||||
@@ -1,7 +1,9 @@
|
|||||||
use super::macros::to_ast_node;
|
use super::macros::to_ast_node;
|
||||||
use super::CenterBlock;
|
use super::CenterBlock;
|
||||||
|
use super::PostBlank;
|
||||||
use super::QuoteBlock;
|
use super::QuoteBlock;
|
||||||
use super::SpecialBlock;
|
use super::SpecialBlock;
|
||||||
|
use super::StandardProperties;
|
||||||
use crate::types::AngleLink;
|
use crate::types::AngleLink;
|
||||||
use crate::types::BabelCall;
|
use crate::types::BabelCall;
|
||||||
use crate::types::Bold;
|
use crate::types::Bold;
|
||||||
@@ -24,7 +26,6 @@ use crate::types::ExportSnippet;
|
|||||||
use crate::types::FixedWidthArea;
|
use crate::types::FixedWidthArea;
|
||||||
use crate::types::FootnoteDefinition;
|
use crate::types::FootnoteDefinition;
|
||||||
use crate::types::FootnoteReference;
|
use crate::types::FootnoteReference;
|
||||||
use crate::types::GetStandardProperties;
|
|
||||||
use crate::types::Heading;
|
use crate::types::Heading;
|
||||||
use crate::types::HorizontalRule;
|
use crate::types::HorizontalRule;
|
||||||
use crate::types::InlineBabelCall;
|
use crate::types::InlineBabelCall;
|
||||||
@@ -259,67 +260,193 @@ to_ast_node!(&'r Superscript<'s>, AstNode::Superscript);
|
|||||||
to_ast_node!(&'r TableCell<'s>, AstNode::TableCell);
|
to_ast_node!(&'r TableCell<'s>, AstNode::TableCell);
|
||||||
to_ast_node!(&'r Timestamp<'s>, AstNode::Timestamp);
|
to_ast_node!(&'r Timestamp<'s>, AstNode::Timestamp);
|
||||||
|
|
||||||
impl<'r, 's> GetStandardProperties<'s> for AstNode<'r, 's> {
|
impl<'r, 's> StandardProperties<'s> for AstNode<'r, 's> {
|
||||||
fn get_standard_properties<'b>(&'b self) -> &'b dyn crate::types::StandardProperties<'s> {
|
fn get_source<'b>(&'b self) -> &'s str {
|
||||||
match self {
|
match self {
|
||||||
AstNode::Document(inner) => *inner,
|
AstNode::Document(inner) => inner.get_source(),
|
||||||
AstNode::Heading(inner) => *inner,
|
AstNode::Heading(inner) => inner.get_source(),
|
||||||
AstNode::Section(inner) => *inner,
|
AstNode::Section(inner) => inner.get_source(),
|
||||||
AstNode::Paragraph(inner) => *inner,
|
AstNode::Paragraph(inner) => inner.get_source(),
|
||||||
AstNode::PlainList(inner) => *inner,
|
AstNode::PlainList(inner) => inner.get_source(),
|
||||||
AstNode::PlainListItem(inner) => *inner,
|
AstNode::PlainListItem(inner) => inner.get_source(),
|
||||||
AstNode::CenterBlock(inner) => *inner,
|
AstNode::CenterBlock(inner) => inner.get_source(),
|
||||||
AstNode::QuoteBlock(inner) => *inner,
|
AstNode::QuoteBlock(inner) => inner.get_source(),
|
||||||
AstNode::SpecialBlock(inner) => *inner,
|
AstNode::SpecialBlock(inner) => inner.get_source(),
|
||||||
AstNode::DynamicBlock(inner) => *inner,
|
AstNode::DynamicBlock(inner) => inner.get_source(),
|
||||||
AstNode::FootnoteDefinition(inner) => *inner,
|
AstNode::FootnoteDefinition(inner) => inner.get_source(),
|
||||||
AstNode::Comment(inner) => *inner,
|
AstNode::Comment(inner) => inner.get_source(),
|
||||||
AstNode::Drawer(inner) => *inner,
|
AstNode::Drawer(inner) => inner.get_source(),
|
||||||
AstNode::PropertyDrawer(inner) => *inner,
|
AstNode::PropertyDrawer(inner) => inner.get_source(),
|
||||||
AstNode::NodeProperty(inner) => *inner,
|
AstNode::NodeProperty(inner) => inner.get_source(),
|
||||||
AstNode::Table(inner) => *inner,
|
AstNode::Table(inner) => inner.get_source(),
|
||||||
AstNode::TableRow(inner) => *inner,
|
AstNode::TableRow(inner) => inner.get_source(),
|
||||||
AstNode::VerseBlock(inner) => *inner,
|
AstNode::VerseBlock(inner) => inner.get_source(),
|
||||||
AstNode::CommentBlock(inner) => *inner,
|
AstNode::CommentBlock(inner) => inner.get_source(),
|
||||||
AstNode::ExampleBlock(inner) => *inner,
|
AstNode::ExampleBlock(inner) => inner.get_source(),
|
||||||
AstNode::ExportBlock(inner) => *inner,
|
AstNode::ExportBlock(inner) => inner.get_source(),
|
||||||
AstNode::SrcBlock(inner) => *inner,
|
AstNode::SrcBlock(inner) => inner.get_source(),
|
||||||
AstNode::Clock(inner) => *inner,
|
AstNode::Clock(inner) => inner.get_source(),
|
||||||
AstNode::DiarySexp(inner) => *inner,
|
AstNode::DiarySexp(inner) => inner.get_source(),
|
||||||
AstNode::Planning(inner) => *inner,
|
AstNode::Planning(inner) => inner.get_source(),
|
||||||
AstNode::FixedWidthArea(inner) => *inner,
|
AstNode::FixedWidthArea(inner) => inner.get_source(),
|
||||||
AstNode::HorizontalRule(inner) => *inner,
|
AstNode::HorizontalRule(inner) => inner.get_source(),
|
||||||
AstNode::Keyword(inner) => *inner,
|
AstNode::Keyword(inner) => inner.get_source(),
|
||||||
AstNode::BabelCall(inner) => *inner,
|
AstNode::BabelCall(inner) => inner.get_source(),
|
||||||
AstNode::LatexEnvironment(inner) => *inner,
|
AstNode::LatexEnvironment(inner) => inner.get_source(),
|
||||||
AstNode::Bold(inner) => *inner,
|
AstNode::Bold(inner) => inner.get_source(),
|
||||||
AstNode::Italic(inner) => *inner,
|
AstNode::Italic(inner) => inner.get_source(),
|
||||||
AstNode::Underline(inner) => *inner,
|
AstNode::Underline(inner) => inner.get_source(),
|
||||||
AstNode::StrikeThrough(inner) => *inner,
|
AstNode::StrikeThrough(inner) => inner.get_source(),
|
||||||
AstNode::Code(inner) => *inner,
|
AstNode::Code(inner) => inner.get_source(),
|
||||||
AstNode::Verbatim(inner) => *inner,
|
AstNode::Verbatim(inner) => inner.get_source(),
|
||||||
AstNode::PlainText(inner) => *inner,
|
AstNode::PlainText(inner) => inner.get_source(),
|
||||||
AstNode::RegularLink(inner) => *inner,
|
AstNode::RegularLink(inner) => inner.get_source(),
|
||||||
AstNode::RadioLink(inner) => *inner,
|
AstNode::RadioLink(inner) => inner.get_source(),
|
||||||
AstNode::RadioTarget(inner) => *inner,
|
AstNode::RadioTarget(inner) => inner.get_source(),
|
||||||
AstNode::PlainLink(inner) => *inner,
|
AstNode::PlainLink(inner) => inner.get_source(),
|
||||||
AstNode::AngleLink(inner) => *inner,
|
AstNode::AngleLink(inner) => inner.get_source(),
|
||||||
AstNode::OrgMacro(inner) => *inner,
|
AstNode::OrgMacro(inner) => inner.get_source(),
|
||||||
AstNode::Entity(inner) => *inner,
|
AstNode::Entity(inner) => inner.get_source(),
|
||||||
AstNode::LatexFragment(inner) => *inner,
|
AstNode::LatexFragment(inner) => inner.get_source(),
|
||||||
AstNode::ExportSnippet(inner) => *inner,
|
AstNode::ExportSnippet(inner) => inner.get_source(),
|
||||||
AstNode::FootnoteReference(inner) => *inner,
|
AstNode::FootnoteReference(inner) => inner.get_source(),
|
||||||
AstNode::Citation(inner) => *inner,
|
AstNode::Citation(inner) => inner.get_source(),
|
||||||
AstNode::CitationReference(inner) => *inner,
|
AstNode::CitationReference(inner) => inner.get_source(),
|
||||||
AstNode::InlineBabelCall(inner) => *inner,
|
AstNode::InlineBabelCall(inner) => inner.get_source(),
|
||||||
AstNode::InlineSourceBlock(inner) => *inner,
|
AstNode::InlineSourceBlock(inner) => inner.get_source(),
|
||||||
AstNode::LineBreak(inner) => *inner,
|
AstNode::LineBreak(inner) => inner.get_source(),
|
||||||
AstNode::Target(inner) => *inner,
|
AstNode::Target(inner) => inner.get_source(),
|
||||||
AstNode::StatisticsCookie(inner) => *inner,
|
AstNode::StatisticsCookie(inner) => inner.get_source(),
|
||||||
AstNode::Subscript(inner) => *inner,
|
AstNode::Subscript(inner) => inner.get_source(),
|
||||||
AstNode::Superscript(inner) => *inner,
|
AstNode::Superscript(inner) => inner.get_source(),
|
||||||
AstNode::TableCell(inner) => *inner,
|
AstNode::TableCell(inner) => inner.get_source(),
|
||||||
AstNode::Timestamp(inner) => *inner,
|
AstNode::Timestamp(inner) => inner.get_source(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_contents<'b>(&'b self) -> Option<&'s str> {
|
||||||
|
match self {
|
||||||
|
AstNode::Document(inner) => inner.get_contents(),
|
||||||
|
AstNode::Heading(inner) => inner.get_contents(),
|
||||||
|
AstNode::Section(inner) => inner.get_contents(),
|
||||||
|
AstNode::Paragraph(inner) => inner.get_contents(),
|
||||||
|
AstNode::PlainList(inner) => inner.get_contents(),
|
||||||
|
AstNode::PlainListItem(inner) => inner.get_contents(),
|
||||||
|
AstNode::CenterBlock(inner) => inner.get_contents(),
|
||||||
|
AstNode::QuoteBlock(inner) => inner.get_contents(),
|
||||||
|
AstNode::SpecialBlock(inner) => inner.get_contents(),
|
||||||
|
AstNode::DynamicBlock(inner) => inner.get_contents(),
|
||||||
|
AstNode::FootnoteDefinition(inner) => inner.get_contents(),
|
||||||
|
AstNode::Comment(inner) => inner.get_contents(),
|
||||||
|
AstNode::Drawer(inner) => inner.get_contents(),
|
||||||
|
AstNode::PropertyDrawer(inner) => inner.get_contents(),
|
||||||
|
AstNode::NodeProperty(inner) => inner.get_contents(),
|
||||||
|
AstNode::Table(inner) => inner.get_contents(),
|
||||||
|
AstNode::TableRow(inner) => inner.get_contents(),
|
||||||
|
AstNode::VerseBlock(inner) => inner.get_contents(),
|
||||||
|
AstNode::CommentBlock(inner) => inner.get_contents(),
|
||||||
|
AstNode::ExampleBlock(inner) => inner.get_contents(),
|
||||||
|
AstNode::ExportBlock(inner) => inner.get_contents(),
|
||||||
|
AstNode::SrcBlock(inner) => inner.get_contents(),
|
||||||
|
AstNode::Clock(inner) => inner.get_contents(),
|
||||||
|
AstNode::DiarySexp(inner) => inner.get_contents(),
|
||||||
|
AstNode::Planning(inner) => inner.get_contents(),
|
||||||
|
AstNode::FixedWidthArea(inner) => inner.get_contents(),
|
||||||
|
AstNode::HorizontalRule(inner) => inner.get_contents(),
|
||||||
|
AstNode::Keyword(inner) => inner.get_contents(),
|
||||||
|
AstNode::BabelCall(inner) => inner.get_contents(),
|
||||||
|
AstNode::LatexEnvironment(inner) => inner.get_contents(),
|
||||||
|
AstNode::Bold(inner) => inner.get_contents(),
|
||||||
|
AstNode::Italic(inner) => inner.get_contents(),
|
||||||
|
AstNode::Underline(inner) => inner.get_contents(),
|
||||||
|
AstNode::StrikeThrough(inner) => inner.get_contents(),
|
||||||
|
AstNode::Code(inner) => inner.get_contents(),
|
||||||
|
AstNode::Verbatim(inner) => inner.get_contents(),
|
||||||
|
AstNode::PlainText(inner) => inner.get_contents(),
|
||||||
|
AstNode::RegularLink(inner) => inner.get_contents(),
|
||||||
|
AstNode::RadioLink(inner) => inner.get_contents(),
|
||||||
|
AstNode::RadioTarget(inner) => inner.get_contents(),
|
||||||
|
AstNode::PlainLink(inner) => inner.get_contents(),
|
||||||
|
AstNode::AngleLink(inner) => inner.get_contents(),
|
||||||
|
AstNode::OrgMacro(inner) => inner.get_contents(),
|
||||||
|
AstNode::Entity(inner) => inner.get_contents(),
|
||||||
|
AstNode::LatexFragment(inner) => inner.get_contents(),
|
||||||
|
AstNode::ExportSnippet(inner) => inner.get_contents(),
|
||||||
|
AstNode::FootnoteReference(inner) => inner.get_contents(),
|
||||||
|
AstNode::Citation(inner) => inner.get_contents(),
|
||||||
|
AstNode::CitationReference(inner) => inner.get_contents(),
|
||||||
|
AstNode::InlineBabelCall(inner) => inner.get_contents(),
|
||||||
|
AstNode::InlineSourceBlock(inner) => inner.get_contents(),
|
||||||
|
AstNode::LineBreak(inner) => inner.get_contents(),
|
||||||
|
AstNode::Target(inner) => inner.get_contents(),
|
||||||
|
AstNode::StatisticsCookie(inner) => inner.get_contents(),
|
||||||
|
AstNode::Subscript(inner) => inner.get_contents(),
|
||||||
|
AstNode::Superscript(inner) => inner.get_contents(),
|
||||||
|
AstNode::TableCell(inner) => inner.get_contents(),
|
||||||
|
AstNode::Timestamp(inner) => inner.get_contents(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_post_blank(&self) -> PostBlank {
|
||||||
|
match self {
|
||||||
|
AstNode::Document(inner) => inner.get_post_blank(),
|
||||||
|
AstNode::Heading(inner) => inner.get_post_blank(),
|
||||||
|
AstNode::Section(inner) => inner.get_post_blank(),
|
||||||
|
AstNode::Paragraph(inner) => inner.get_post_blank(),
|
||||||
|
AstNode::PlainList(inner) => inner.get_post_blank(),
|
||||||
|
AstNode::PlainListItem(inner) => inner.get_post_blank(),
|
||||||
|
AstNode::CenterBlock(inner) => inner.get_post_blank(),
|
||||||
|
AstNode::QuoteBlock(inner) => inner.get_post_blank(),
|
||||||
|
AstNode::SpecialBlock(inner) => inner.get_post_blank(),
|
||||||
|
AstNode::DynamicBlock(inner) => inner.get_post_blank(),
|
||||||
|
AstNode::FootnoteDefinition(inner) => inner.get_post_blank(),
|
||||||
|
AstNode::Comment(inner) => inner.get_post_blank(),
|
||||||
|
AstNode::Drawer(inner) => inner.get_post_blank(),
|
||||||
|
AstNode::PropertyDrawer(inner) => inner.get_post_blank(),
|
||||||
|
AstNode::NodeProperty(inner) => inner.get_post_blank(),
|
||||||
|
AstNode::Table(inner) => inner.get_post_blank(),
|
||||||
|
AstNode::TableRow(inner) => inner.get_post_blank(),
|
||||||
|
AstNode::VerseBlock(inner) => inner.get_post_blank(),
|
||||||
|
AstNode::CommentBlock(inner) => inner.get_post_blank(),
|
||||||
|
AstNode::ExampleBlock(inner) => inner.get_post_blank(),
|
||||||
|
AstNode::ExportBlock(inner) => inner.get_post_blank(),
|
||||||
|
AstNode::SrcBlock(inner) => inner.get_post_blank(),
|
||||||
|
AstNode::Clock(inner) => inner.get_post_blank(),
|
||||||
|
AstNode::DiarySexp(inner) => inner.get_post_blank(),
|
||||||
|
AstNode::Planning(inner) => inner.get_post_blank(),
|
||||||
|
AstNode::FixedWidthArea(inner) => inner.get_post_blank(),
|
||||||
|
AstNode::HorizontalRule(inner) => inner.get_post_blank(),
|
||||||
|
AstNode::Keyword(inner) => inner.get_post_blank(),
|
||||||
|
AstNode::BabelCall(inner) => inner.get_post_blank(),
|
||||||
|
AstNode::LatexEnvironment(inner) => inner.get_post_blank(),
|
||||||
|
AstNode::Bold(inner) => inner.get_post_blank(),
|
||||||
|
AstNode::Italic(inner) => inner.get_post_blank(),
|
||||||
|
AstNode::Underline(inner) => inner.get_post_blank(),
|
||||||
|
AstNode::StrikeThrough(inner) => inner.get_post_blank(),
|
||||||
|
AstNode::Code(inner) => inner.get_post_blank(),
|
||||||
|
AstNode::Verbatim(inner) => inner.get_post_blank(),
|
||||||
|
AstNode::PlainText(inner) => inner.get_post_blank(),
|
||||||
|
AstNode::RegularLink(inner) => inner.get_post_blank(),
|
||||||
|
AstNode::RadioLink(inner) => inner.get_post_blank(),
|
||||||
|
AstNode::RadioTarget(inner) => inner.get_post_blank(),
|
||||||
|
AstNode::PlainLink(inner) => inner.get_post_blank(),
|
||||||
|
AstNode::AngleLink(inner) => inner.get_post_blank(),
|
||||||
|
AstNode::OrgMacro(inner) => inner.get_post_blank(),
|
||||||
|
AstNode::Entity(inner) => inner.get_post_blank(),
|
||||||
|
AstNode::LatexFragment(inner) => inner.get_post_blank(),
|
||||||
|
AstNode::ExportSnippet(inner) => inner.get_post_blank(),
|
||||||
|
AstNode::FootnoteReference(inner) => inner.get_post_blank(),
|
||||||
|
AstNode::Citation(inner) => inner.get_post_blank(),
|
||||||
|
AstNode::CitationReference(inner) => inner.get_post_blank(),
|
||||||
|
AstNode::InlineBabelCall(inner) => inner.get_post_blank(),
|
||||||
|
AstNode::InlineSourceBlock(inner) => inner.get_post_blank(),
|
||||||
|
AstNode::LineBreak(inner) => inner.get_post_blank(),
|
||||||
|
AstNode::Target(inner) => inner.get_post_blank(),
|
||||||
|
AstNode::StatisticsCookie(inner) => inner.get_post_blank(),
|
||||||
|
AstNode::Subscript(inner) => inner.get_post_blank(),
|
||||||
|
AstNode::Superscript(inner) => inner.get_post_blank(),
|
||||||
|
AstNode::TableCell(inner) => inner.get_post_blank(),
|
||||||
|
AstNode::Timestamp(inner) => inner.get_post_blank(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
|
|
||||||
use super::Element;
|
use super::Element;
|
||||||
use super::GetStandardProperties;
|
|
||||||
use super::NodeProperty;
|
use super::NodeProperty;
|
||||||
use super::Object;
|
use super::Object;
|
||||||
|
use super::PostBlank;
|
||||||
use super::StandardProperties;
|
use super::StandardProperties;
|
||||||
use super::Timestamp;
|
use super::Timestamp;
|
||||||
|
|
||||||
@@ -17,6 +17,7 @@ pub struct Document<'s> {
|
|||||||
pub path: Option<PathBuf>,
|
pub path: Option<PathBuf>,
|
||||||
pub zeroth_section: Option<Section<'s>>,
|
pub zeroth_section: Option<Section<'s>>,
|
||||||
pub children: Vec<Heading<'s>>,
|
pub children: Vec<Heading<'s>>,
|
||||||
|
pub contents: &'s str,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
@@ -34,11 +35,14 @@ pub struct Heading<'s> {
|
|||||||
pub scheduled: Option<Timestamp<'s>>,
|
pub scheduled: Option<Timestamp<'s>>,
|
||||||
pub deadline: Option<Timestamp<'s>>,
|
pub deadline: Option<Timestamp<'s>>,
|
||||||
pub closed: Option<Timestamp<'s>>,
|
pub closed: Option<Timestamp<'s>>,
|
||||||
|
pub contents: Option<&'s str>,
|
||||||
|
pub post_blank: Option<&'s str>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct Section<'s> {
|
pub struct Section<'s> {
|
||||||
pub source: &'s str,
|
pub source: &'s str,
|
||||||
|
pub post_blank: Option<&'s str>,
|
||||||
pub children: Vec<Element<'s>>,
|
pub children: Vec<Element<'s>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -54,41 +58,60 @@ pub enum TodoKeywordType {
|
|||||||
Done,
|
Done,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'s> GetStandardProperties<'s> for DocumentElement<'s> {
|
|
||||||
fn get_standard_properties<'b>(&'b self) -> &'b dyn StandardProperties<'s> {
|
|
||||||
match self {
|
|
||||||
DocumentElement::Heading(inner) => inner,
|
|
||||||
DocumentElement::Section(inner) => inner,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'s> StandardProperties<'s> for Document<'s> {
|
impl<'s> StandardProperties<'s> for Document<'s> {
|
||||||
fn get_source<'b>(&'b self) -> &'s str {
|
fn get_source<'b>(&'b self) -> &'s str {
|
||||||
self.source
|
self.source
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn get_contents<'b>(&'b self) -> Option<&'s str> {
|
||||||
|
Some(self.contents)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_post_blank(&self) -> PostBlank {
|
||||||
|
0
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'s> StandardProperties<'s> for Section<'s> {
|
impl<'s> StandardProperties<'s> for Section<'s> {
|
||||||
fn get_source<'b>(&'b self) -> &'s str {
|
fn get_source<'b>(&'b self) -> &'s str {
|
||||||
self.source
|
self.source
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn get_contents<'b>(&'b self) -> Option<&'s str> {
|
||||||
|
Some(self.source)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_post_blank(&self) -> PostBlank {
|
||||||
|
self.post_blank
|
||||||
|
.map(|text| text.lines().count())
|
||||||
|
.unwrap_or(0)
|
||||||
|
.try_into()
|
||||||
|
.expect("Too much post-blank to fit into a PostBlank.")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'s> StandardProperties<'s> for Heading<'s> {
|
impl<'s> StandardProperties<'s> for Heading<'s> {
|
||||||
fn get_source<'b>(&'b self) -> &'s str {
|
fn get_source<'b>(&'b self) -> &'s str {
|
||||||
self.source
|
self.source
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn get_contents<'b>(&'b self) -> Option<&'s str> {
|
||||||
|
self.contents
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_post_blank(&self) -> PostBlank {
|
||||||
|
self.post_blank
|
||||||
|
.map(|text| text.lines().count())
|
||||||
|
.unwrap_or(0)
|
||||||
|
.try_into()
|
||||||
|
.expect("Too much post-blank to fit into a PostBlank.")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'s> Heading<'s> {
|
impl<'s> Heading<'s> {
|
||||||
pub fn get_raw_value(&self) -> String {
|
pub fn get_raw_value(&self) -> String {
|
||||||
// TODO: I think this could just return a string slice instead of an owned string.
|
// TODO: I think this could just return a string slice instead of an owned string.
|
||||||
let title_source: String = self
|
let title_source: String = self.title.iter().map(|obj| obj.get_source()).collect();
|
||||||
.title
|
|
||||||
.iter()
|
|
||||||
.map(|obj| obj.get_standard_properties().get_source())
|
|
||||||
.collect();
|
|
||||||
title_source
|
title_source
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -132,3 +155,26 @@ impl<'s> Document<'s> {
|
|||||||
.flat_map(|property_drawer| property_drawer.children.iter())
|
.flat_map(|property_drawer| property_drawer.children.iter())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<'s> StandardProperties<'s> for DocumentElement<'s> {
|
||||||
|
fn get_source<'b>(&'b self) -> &'s str {
|
||||||
|
match self {
|
||||||
|
DocumentElement::Heading(inner) => inner.get_source(),
|
||||||
|
DocumentElement::Section(inner) => inner.get_source(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_contents<'b>(&'b self) -> Option<&'s str> {
|
||||||
|
match self {
|
||||||
|
DocumentElement::Heading(inner) => inner.get_contents(),
|
||||||
|
DocumentElement::Section(inner) => inner.get_contents(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_post_blank(&self) -> PostBlank {
|
||||||
|
match self {
|
||||||
|
DocumentElement::Heading(inner) => inner.get_post_blank(),
|
||||||
|
DocumentElement::Section(inner) => inner.get_post_blank(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -20,9 +20,8 @@ use super::lesser_element::SrcBlock;
|
|||||||
use super::lesser_element::VerseBlock;
|
use super::lesser_element::VerseBlock;
|
||||||
use super::CenterBlock;
|
use super::CenterBlock;
|
||||||
use super::Drawer;
|
use super::Drawer;
|
||||||
use super::GetStandardProperties;
|
use super::PostBlank;
|
||||||
use super::QuoteBlock;
|
use super::QuoteBlock;
|
||||||
use super::SetSource;
|
|
||||||
use super::SpecialBlock;
|
use super::SpecialBlock;
|
||||||
use super::StandardProperties;
|
use super::StandardProperties;
|
||||||
|
|
||||||
@@ -55,65 +54,91 @@ pub enum Element<'s> {
|
|||||||
LatexEnvironment(LatexEnvironment<'s>),
|
LatexEnvironment(LatexEnvironment<'s>),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'s> SetSource<'s> for Element<'s> {
|
impl<'s> StandardProperties<'s> for Element<'s> {
|
||||||
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
|
fn get_source<'b>(&'b self) -> &'s str {
|
||||||
fn set_source(&mut self, source: &'s str) {
|
|
||||||
match self {
|
match self {
|
||||||
Element::Paragraph(obj) => obj.source = source,
|
Element::Paragraph(inner) => inner.get_source(),
|
||||||
Element::PlainList(obj) => obj.source = source,
|
Element::PlainList(inner) => inner.get_source(),
|
||||||
Element::CenterBlock(obj) => obj.source = source,
|
Element::CenterBlock(inner) => inner.get_source(),
|
||||||
Element::QuoteBlock(obj) => obj.source = source,
|
Element::QuoteBlock(inner) => inner.get_source(),
|
||||||
Element::SpecialBlock(obj) => obj.source = source,
|
Element::SpecialBlock(inner) => inner.get_source(),
|
||||||
Element::DynamicBlock(obj) => obj.source = source,
|
Element::DynamicBlock(inner) => inner.get_source(),
|
||||||
Element::FootnoteDefinition(obj) => obj.source = source,
|
Element::FootnoteDefinition(inner) => inner.get_source(),
|
||||||
Element::Comment(obj) => obj.source = source,
|
Element::Comment(inner) => inner.get_source(),
|
||||||
Element::Drawer(obj) => obj.source = source,
|
Element::Drawer(inner) => inner.get_source(),
|
||||||
Element::PropertyDrawer(obj) => obj.source = source,
|
Element::PropertyDrawer(inner) => inner.get_source(),
|
||||||
Element::Table(obj) => obj.source = source,
|
Element::Table(inner) => inner.get_source(),
|
||||||
Element::VerseBlock(obj) => obj.source = source,
|
Element::VerseBlock(inner) => inner.get_source(),
|
||||||
Element::CommentBlock(obj) => obj.source = source,
|
Element::CommentBlock(inner) => inner.get_source(),
|
||||||
Element::ExampleBlock(obj) => obj.source = source,
|
Element::ExampleBlock(inner) => inner.get_source(),
|
||||||
Element::ExportBlock(obj) => obj.source = source,
|
Element::ExportBlock(inner) => inner.get_source(),
|
||||||
Element::SrcBlock(obj) => obj.source = source,
|
Element::SrcBlock(inner) => inner.get_source(),
|
||||||
Element::Clock(obj) => obj.source = source,
|
Element::Clock(inner) => inner.get_source(),
|
||||||
Element::DiarySexp(obj) => obj.source = source,
|
Element::DiarySexp(inner) => inner.get_source(),
|
||||||
Element::Planning(obj) => obj.source = source,
|
Element::Planning(inner) => inner.get_source(),
|
||||||
Element::FixedWidthArea(obj) => obj.source = source,
|
Element::FixedWidthArea(inner) => inner.get_source(),
|
||||||
Element::HorizontalRule(obj) => obj.source = source,
|
Element::HorizontalRule(inner) => inner.get_source(),
|
||||||
Element::Keyword(obj) => obj.source = source,
|
Element::Keyword(inner) => inner.get_source(),
|
||||||
Element::BabelCall(obj) => obj.source = source,
|
Element::BabelCall(inner) => inner.get_source(),
|
||||||
Element::LatexEnvironment(obj) => obj.source = source,
|
Element::LatexEnvironment(inner) => inner.get_source(),
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'s> GetStandardProperties<'s> for Element<'s> {
|
fn get_contents<'b>(&'b self) -> Option<&'s str> {
|
||||||
fn get_standard_properties<'b>(&'b self) -> &'b dyn StandardProperties<'s> {
|
|
||||||
match self {
|
match self {
|
||||||
Element::Paragraph(inner) => inner,
|
Element::Paragraph(inner) => inner.get_contents(),
|
||||||
Element::PlainList(inner) => inner,
|
Element::PlainList(inner) => inner.get_contents(),
|
||||||
Element::CenterBlock(inner) => inner,
|
Element::CenterBlock(inner) => inner.get_contents(),
|
||||||
Element::QuoteBlock(inner) => inner,
|
Element::QuoteBlock(inner) => inner.get_contents(),
|
||||||
Element::SpecialBlock(inner) => inner,
|
Element::SpecialBlock(inner) => inner.get_contents(),
|
||||||
Element::DynamicBlock(inner) => inner,
|
Element::DynamicBlock(inner) => inner.get_contents(),
|
||||||
Element::FootnoteDefinition(inner) => inner,
|
Element::FootnoteDefinition(inner) => inner.get_contents(),
|
||||||
Element::Comment(inner) => inner,
|
Element::Comment(inner) => inner.get_contents(),
|
||||||
Element::Drawer(inner) => inner,
|
Element::Drawer(inner) => inner.get_contents(),
|
||||||
Element::PropertyDrawer(inner) => inner,
|
Element::PropertyDrawer(inner) => inner.get_contents(),
|
||||||
Element::Table(inner) => inner,
|
Element::Table(inner) => inner.get_contents(),
|
||||||
Element::VerseBlock(inner) => inner,
|
Element::VerseBlock(inner) => inner.get_contents(),
|
||||||
Element::CommentBlock(inner) => inner,
|
Element::CommentBlock(inner) => inner.get_contents(),
|
||||||
Element::ExampleBlock(inner) => inner,
|
Element::ExampleBlock(inner) => inner.get_contents(),
|
||||||
Element::ExportBlock(inner) => inner,
|
Element::ExportBlock(inner) => inner.get_contents(),
|
||||||
Element::SrcBlock(inner) => inner,
|
Element::SrcBlock(inner) => inner.get_contents(),
|
||||||
Element::Clock(inner) => inner,
|
Element::Clock(inner) => inner.get_contents(),
|
||||||
Element::DiarySexp(inner) => inner,
|
Element::DiarySexp(inner) => inner.get_contents(),
|
||||||
Element::Planning(inner) => inner,
|
Element::Planning(inner) => inner.get_contents(),
|
||||||
Element::FixedWidthArea(inner) => inner,
|
Element::FixedWidthArea(inner) => inner.get_contents(),
|
||||||
Element::HorizontalRule(inner) => inner,
|
Element::HorizontalRule(inner) => inner.get_contents(),
|
||||||
Element::Keyword(inner) => inner,
|
Element::Keyword(inner) => inner.get_contents(),
|
||||||
Element::BabelCall(inner) => inner,
|
Element::BabelCall(inner) => inner.get_contents(),
|
||||||
Element::LatexEnvironment(inner) => inner,
|
Element::LatexEnvironment(inner) => inner.get_contents(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_post_blank(&self) -> PostBlank {
|
||||||
|
match self {
|
||||||
|
Element::Paragraph(inner) => inner.get_post_blank(),
|
||||||
|
Element::PlainList(inner) => inner.get_post_blank(),
|
||||||
|
Element::CenterBlock(inner) => inner.get_post_blank(),
|
||||||
|
Element::QuoteBlock(inner) => inner.get_post_blank(),
|
||||||
|
Element::SpecialBlock(inner) => inner.get_post_blank(),
|
||||||
|
Element::DynamicBlock(inner) => inner.get_post_blank(),
|
||||||
|
Element::FootnoteDefinition(inner) => inner.get_post_blank(),
|
||||||
|
Element::Comment(inner) => inner.get_post_blank(),
|
||||||
|
Element::Drawer(inner) => inner.get_post_blank(),
|
||||||
|
Element::PropertyDrawer(inner) => inner.get_post_blank(),
|
||||||
|
Element::Table(inner) => inner.get_post_blank(),
|
||||||
|
Element::VerseBlock(inner) => inner.get_post_blank(),
|
||||||
|
Element::CommentBlock(inner) => inner.get_post_blank(),
|
||||||
|
Element::ExampleBlock(inner) => inner.get_post_blank(),
|
||||||
|
Element::ExportBlock(inner) => inner.get_post_blank(),
|
||||||
|
Element::SrcBlock(inner) => inner.get_post_blank(),
|
||||||
|
Element::Clock(inner) => inner.get_post_blank(),
|
||||||
|
Element::DiarySexp(inner) => inner.get_post_blank(),
|
||||||
|
Element::Planning(inner) => inner.get_post_blank(),
|
||||||
|
Element::FixedWidthArea(inner) => inner.get_post_blank(),
|
||||||
|
Element::HorizontalRule(inner) => inner.get_post_blank(),
|
||||||
|
Element::Keyword(inner) => inner.get_post_blank(),
|
||||||
|
Element::BabelCall(inner) => inner.get_post_blank(),
|
||||||
|
Element::LatexEnvironment(inner) => inner.get_post_blank(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,12 +0,0 @@
|
|||||||
use super::StandardProperties;
|
|
||||||
|
|
||||||
pub trait GetStandardProperties<'s> {
|
|
||||||
// TODO: Can I eliminate this dynamic dispatch, perhaps using nominal generic structs? Low prioritiy since this is not used during parsing.
|
|
||||||
fn get_standard_properties<'b>(&'b self) -> &'b dyn StandardProperties<'s>;
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'s, I: StandardProperties<'s>> GetStandardProperties<'s> for I {
|
|
||||||
fn get_standard_properties<'b>(&'b self) -> &'b dyn StandardProperties<'s> {
|
|
||||||
self
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -4,6 +4,7 @@ use super::lesser_element::TableCell;
|
|||||||
use super::AffiliatedKeywords;
|
use super::AffiliatedKeywords;
|
||||||
use super::Keyword;
|
use super::Keyword;
|
||||||
use super::Object;
|
use super::Object;
|
||||||
|
use super::PostBlank;
|
||||||
use super::StandardProperties;
|
use super::StandardProperties;
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
@@ -12,6 +13,8 @@ pub struct PlainList<'s> {
|
|||||||
pub affiliated_keywords: AffiliatedKeywords<'s>,
|
pub affiliated_keywords: AffiliatedKeywords<'s>,
|
||||||
pub list_type: PlainListType,
|
pub list_type: PlainListType,
|
||||||
pub children: Vec<PlainListItem<'s>>,
|
pub children: Vec<PlainListItem<'s>>,
|
||||||
|
pub contents: Option<&'s str>, // TODO: Can contents ever be None?
|
||||||
|
pub post_blank: Option<&'s str>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Copy, Clone)]
|
#[derive(Debug, Copy, Clone)]
|
||||||
@@ -34,6 +37,8 @@ pub struct PlainListItem<'s> {
|
|||||||
pub tag: Vec<Object<'s>>,
|
pub tag: Vec<Object<'s>>,
|
||||||
pub pre_blank: PlainListItemPreBlank,
|
pub pre_blank: PlainListItemPreBlank,
|
||||||
pub children: Vec<Element<'s>>,
|
pub children: Vec<Element<'s>>,
|
||||||
|
pub contents: Option<&'s str>,
|
||||||
|
pub post_blank: Option<&'s str>,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub type PlainListItemCounter = u16;
|
pub type PlainListItemCounter = u16;
|
||||||
@@ -51,6 +56,8 @@ pub struct CenterBlock<'s> {
|
|||||||
pub source: &'s str,
|
pub source: &'s str,
|
||||||
pub affiliated_keywords: AffiliatedKeywords<'s>,
|
pub affiliated_keywords: AffiliatedKeywords<'s>,
|
||||||
pub children: Vec<Element<'s>>,
|
pub children: Vec<Element<'s>>,
|
||||||
|
pub contents: Option<&'s str>,
|
||||||
|
pub post_blank: Option<&'s str>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
@@ -58,6 +65,8 @@ pub struct QuoteBlock<'s> {
|
|||||||
pub source: &'s str,
|
pub source: &'s str,
|
||||||
pub affiliated_keywords: AffiliatedKeywords<'s>,
|
pub affiliated_keywords: AffiliatedKeywords<'s>,
|
||||||
pub children: Vec<Element<'s>>,
|
pub children: Vec<Element<'s>>,
|
||||||
|
pub contents: Option<&'s str>,
|
||||||
|
pub post_blank: Option<&'s str>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
@@ -67,6 +76,8 @@ pub struct SpecialBlock<'s> {
|
|||||||
pub block_type: &'s str,
|
pub block_type: &'s str,
|
||||||
pub parameters: Option<&'s str>,
|
pub parameters: Option<&'s str>,
|
||||||
pub children: Vec<Element<'s>>,
|
pub children: Vec<Element<'s>>,
|
||||||
|
pub contents: Option<&'s str>,
|
||||||
|
pub post_blank: Option<&'s str>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
@@ -76,11 +87,15 @@ pub struct DynamicBlock<'s> {
|
|||||||
pub block_name: &'s str,
|
pub block_name: &'s str,
|
||||||
pub parameters: Option<&'s str>,
|
pub parameters: Option<&'s str>,
|
||||||
pub children: Vec<Element<'s>>,
|
pub children: Vec<Element<'s>>,
|
||||||
|
pub contents: Option<&'s str>,
|
||||||
|
pub post_blank: Option<&'s str>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct FootnoteDefinition<'s> {
|
pub struct FootnoteDefinition<'s> {
|
||||||
pub source: &'s str,
|
pub source: &'s str,
|
||||||
|
pub contents: Option<&'s str>,
|
||||||
|
pub post_blank: Option<&'s str>,
|
||||||
pub affiliated_keywords: AffiliatedKeywords<'s>,
|
pub affiliated_keywords: AffiliatedKeywords<'s>,
|
||||||
pub label: &'s str,
|
pub label: &'s str,
|
||||||
pub children: Vec<Element<'s>>,
|
pub children: Vec<Element<'s>>,
|
||||||
@@ -92,12 +107,16 @@ pub struct Drawer<'s> {
|
|||||||
pub affiliated_keywords: AffiliatedKeywords<'s>,
|
pub affiliated_keywords: AffiliatedKeywords<'s>,
|
||||||
pub drawer_name: &'s str,
|
pub drawer_name: &'s str,
|
||||||
pub children: Vec<Element<'s>>,
|
pub children: Vec<Element<'s>>,
|
||||||
|
pub contents: Option<&'s str>,
|
||||||
|
pub post_blank: Option<&'s str>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct PropertyDrawer<'s> {
|
pub struct PropertyDrawer<'s> {
|
||||||
pub source: &'s str,
|
pub source: &'s str,
|
||||||
pub children: Vec<NodeProperty<'s>>,
|
pub children: Vec<NodeProperty<'s>>,
|
||||||
|
pub contents: Option<&'s str>,
|
||||||
|
pub post_blank: Option<&'s str>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
@@ -113,12 +132,15 @@ pub struct Table<'s> {
|
|||||||
pub affiliated_keywords: AffiliatedKeywords<'s>,
|
pub affiliated_keywords: AffiliatedKeywords<'s>,
|
||||||
pub formulas: Vec<Keyword<'s>>,
|
pub formulas: Vec<Keyword<'s>>,
|
||||||
pub children: Vec<TableRow<'s>>,
|
pub children: Vec<TableRow<'s>>,
|
||||||
|
pub contents: &'s str,
|
||||||
|
pub post_blank: Option<&'s str>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct TableRow<'s> {
|
pub struct TableRow<'s> {
|
||||||
pub source: &'s str,
|
pub source: &'s str,
|
||||||
pub children: Vec<TableCell<'s>>,
|
pub children: Vec<TableCell<'s>>,
|
||||||
|
pub contents: Option<&'s str>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
@@ -131,72 +153,208 @@ impl<'s> StandardProperties<'s> for PlainList<'s> {
|
|||||||
fn get_source<'b>(&'b self) -> &'s str {
|
fn get_source<'b>(&'b self) -> &'s str {
|
||||||
self.source
|
self.source
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn get_contents<'b>(&'b self) -> Option<&'s str> {
|
||||||
|
self.contents
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_post_blank(&self) -> PostBlank {
|
||||||
|
self.post_blank
|
||||||
|
.map(|text| text.lines().count())
|
||||||
|
.unwrap_or(0)
|
||||||
|
.try_into()
|
||||||
|
.expect("Too much post-blank to fit into a PostBlank.")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'s> StandardProperties<'s> for PlainListItem<'s> {
|
impl<'s> StandardProperties<'s> for PlainListItem<'s> {
|
||||||
fn get_source<'b>(&'b self) -> &'s str {
|
fn get_source<'b>(&'b self) -> &'s str {
|
||||||
self.source
|
self.source
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn get_contents<'b>(&'b self) -> Option<&'s str> {
|
||||||
|
self.contents
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_post_blank(&self) -> PostBlank {
|
||||||
|
self.post_blank
|
||||||
|
.map(|text| text.lines().count())
|
||||||
|
.unwrap_or(0)
|
||||||
|
.try_into()
|
||||||
|
.expect("Too much post-blank to fit into a PostBlank.")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'s> StandardProperties<'s> for CenterBlock<'s> {
|
impl<'s> StandardProperties<'s> for CenterBlock<'s> {
|
||||||
fn get_source<'b>(&'b self) -> &'s str {
|
fn get_source<'b>(&'b self) -> &'s str {
|
||||||
self.source
|
self.source
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn get_contents<'b>(&'b self) -> Option<&'s str> {
|
||||||
|
self.contents
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_post_blank(&self) -> PostBlank {
|
||||||
|
self.post_blank
|
||||||
|
.map(|text| text.lines().count())
|
||||||
|
.unwrap_or(0)
|
||||||
|
.try_into()
|
||||||
|
.expect("Too much post-blank to fit into a PostBlank.")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'s> StandardProperties<'s> for QuoteBlock<'s> {
|
impl<'s> StandardProperties<'s> for QuoteBlock<'s> {
|
||||||
fn get_source<'b>(&'b self) -> &'s str {
|
fn get_source<'b>(&'b self) -> &'s str {
|
||||||
self.source
|
self.source
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn get_contents<'b>(&'b self) -> Option<&'s str> {
|
||||||
|
self.contents
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_post_blank(&self) -> PostBlank {
|
||||||
|
self.post_blank
|
||||||
|
.map(|text| text.lines().count())
|
||||||
|
.unwrap_or(0)
|
||||||
|
.try_into()
|
||||||
|
.expect("Too much post-blank to fit into a PostBlank.")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'s> StandardProperties<'s> for SpecialBlock<'s> {
|
impl<'s> StandardProperties<'s> for SpecialBlock<'s> {
|
||||||
fn get_source<'b>(&'b self) -> &'s str {
|
fn get_source<'b>(&'b self) -> &'s str {
|
||||||
self.source
|
self.source
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn get_contents<'b>(&'b self) -> Option<&'s str> {
|
||||||
|
self.contents
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_post_blank(&self) -> PostBlank {
|
||||||
|
self.post_blank
|
||||||
|
.map(|text| text.lines().count())
|
||||||
|
.unwrap_or(0)
|
||||||
|
.try_into()
|
||||||
|
.expect("Too much post-blank to fit into a PostBlank.")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'s> StandardProperties<'s> for DynamicBlock<'s> {
|
impl<'s> StandardProperties<'s> for DynamicBlock<'s> {
|
||||||
fn get_source<'b>(&'b self) -> &'s str {
|
fn get_source<'b>(&'b self) -> &'s str {
|
||||||
self.source
|
self.source
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn get_contents<'b>(&'b self) -> Option<&'s str> {
|
||||||
|
self.contents
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_post_blank(&self) -> PostBlank {
|
||||||
|
self.post_blank
|
||||||
|
.map(|text| text.lines().count())
|
||||||
|
.unwrap_or(0)
|
||||||
|
.try_into()
|
||||||
|
.expect("Too much post-blank to fit into a PostBlank.")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'s> StandardProperties<'s> for FootnoteDefinition<'s> {
|
impl<'s> StandardProperties<'s> for FootnoteDefinition<'s> {
|
||||||
fn get_source<'b>(&'b self) -> &'s str {
|
fn get_source<'b>(&'b self) -> &'s str {
|
||||||
self.source
|
self.source
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn get_contents<'b>(&'b self) -> Option<&'s str> {
|
||||||
|
self.contents
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_post_blank(&self) -> PostBlank {
|
||||||
|
self.post_blank
|
||||||
|
.map(|text| text.lines().count())
|
||||||
|
.unwrap_or(0)
|
||||||
|
.try_into()
|
||||||
|
.expect("Too much post-blank to fit into a PostBlank.")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'s> StandardProperties<'s> for Drawer<'s> {
|
impl<'s> StandardProperties<'s> for Drawer<'s> {
|
||||||
fn get_source<'b>(&'b self) -> &'s str {
|
fn get_source<'b>(&'b self) -> &'s str {
|
||||||
self.source
|
self.source
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn get_contents<'b>(&'b self) -> Option<&'s str> {
|
||||||
|
self.contents
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_post_blank(&self) -> PostBlank {
|
||||||
|
self.post_blank
|
||||||
|
.map(|text| text.lines().count())
|
||||||
|
.unwrap_or(0)
|
||||||
|
.try_into()
|
||||||
|
.expect("Too much post-blank to fit into a PostBlank.")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'s> StandardProperties<'s> for PropertyDrawer<'s> {
|
impl<'s> StandardProperties<'s> for PropertyDrawer<'s> {
|
||||||
fn get_source<'b>(&'b self) -> &'s str {
|
fn get_source<'b>(&'b self) -> &'s str {
|
||||||
self.source
|
self.source
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn get_contents<'b>(&'b self) -> Option<&'s str> {
|
||||||
|
self.contents
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_post_blank(&self) -> PostBlank {
|
||||||
|
self.post_blank
|
||||||
|
.map(|text| text.lines().count())
|
||||||
|
.unwrap_or(0)
|
||||||
|
.try_into()
|
||||||
|
.expect("Too much post-blank to fit into a PostBlank.")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'s> StandardProperties<'s> for NodeProperty<'s> {
|
impl<'s> StandardProperties<'s> for NodeProperty<'s> {
|
||||||
fn get_source<'b>(&'b self) -> &'s str {
|
fn get_source<'b>(&'b self) -> &'s str {
|
||||||
self.source
|
self.source
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn get_contents<'b>(&'b self) -> Option<&'s str> {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_post_blank(&self) -> PostBlank {
|
||||||
|
0
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'s> StandardProperties<'s> for Table<'s> {
|
impl<'s> StandardProperties<'s> for Table<'s> {
|
||||||
fn get_source<'b>(&'b self) -> &'s str {
|
fn get_source<'b>(&'b self) -> &'s str {
|
||||||
self.source
|
self.source
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn get_contents<'b>(&'b self) -> Option<&'s str> {
|
||||||
|
Some(self.contents)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_post_blank(&self) -> PostBlank {
|
||||||
|
self.post_blank
|
||||||
|
.map(|text| text.lines().count())
|
||||||
|
.unwrap_or(0)
|
||||||
|
.try_into()
|
||||||
|
.expect("Too much post-blank to fit into a PostBlank.")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'s> StandardProperties<'s> for TableRow<'s> {
|
impl<'s> StandardProperties<'s> for TableRow<'s> {
|
||||||
fn get_source<'b>(&'b self) -> &'s str {
|
fn get_source<'b>(&'b self) -> &'s str {
|
||||||
self.source
|
self.source
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn get_contents<'b>(&'b self) -> Option<&'s str> {
|
||||||
|
self.contents
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_post_blank(&self) -> PostBlank {
|
||||||
|
0
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'s> PlainListItem<'s> {
|
impl<'s> PlainListItem<'s> {
|
||||||
|
|||||||
@@ -1,13 +1,32 @@
|
|||||||
|
use std::borrow::Cow;
|
||||||
|
|
||||||
|
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::map;
|
||||||
|
use nom::combinator::opt;
|
||||||
|
use nom::combinator::peek;
|
||||||
|
use nom::combinator::recognize;
|
||||||
|
use nom::multi::many_till;
|
||||||
|
use nom::sequence::tuple;
|
||||||
|
|
||||||
use super::object::Object;
|
use super::object::Object;
|
||||||
use super::AffiliatedKeywords;
|
use super::AffiliatedKeywords;
|
||||||
use super::GetAffiliatedKeywords;
|
use super::GetAffiliatedKeywords;
|
||||||
use super::PlainText;
|
use super::PlainText;
|
||||||
|
use super::PostBlank;
|
||||||
use super::StandardProperties;
|
use super::StandardProperties;
|
||||||
use super::Timestamp;
|
use super::Timestamp;
|
||||||
|
use crate::error::CustomError;
|
||||||
|
use crate::error::Res;
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct Paragraph<'s> {
|
pub struct Paragraph<'s> {
|
||||||
pub source: &'s str,
|
pub source: &'s str,
|
||||||
|
pub contents: Option<&'s str>,
|
||||||
|
pub post_blank: Option<&'s str>,
|
||||||
pub affiliated_keywords: AffiliatedKeywords<'s>,
|
pub affiliated_keywords: AffiliatedKeywords<'s>,
|
||||||
pub children: Vec<Object<'s>>,
|
pub children: Vec<Object<'s>>,
|
||||||
}
|
}
|
||||||
@@ -16,12 +35,14 @@ pub struct Paragraph<'s> {
|
|||||||
pub struct Comment<'s> {
|
pub struct Comment<'s> {
|
||||||
pub source: &'s str,
|
pub source: &'s str,
|
||||||
pub value: Vec<&'s str>,
|
pub value: Vec<&'s str>,
|
||||||
|
pub post_blank: Option<&'s str>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct TableCell<'s> {
|
pub struct TableCell<'s> {
|
||||||
pub source: &'s str,
|
pub source: &'s str,
|
||||||
pub children: Vec<Object<'s>>,
|
pub children: Vec<Object<'s>>,
|
||||||
|
pub contents: &'s str,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
@@ -30,6 +51,8 @@ pub struct VerseBlock<'s> {
|
|||||||
pub affiliated_keywords: AffiliatedKeywords<'s>,
|
pub affiliated_keywords: AffiliatedKeywords<'s>,
|
||||||
pub data: Option<&'s str>,
|
pub data: Option<&'s str>,
|
||||||
pub children: Vec<Object<'s>>,
|
pub children: Vec<Object<'s>>,
|
||||||
|
pub contents: &'s str,
|
||||||
|
pub post_blank: Option<&'s str>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
@@ -37,6 +60,7 @@ pub struct CommentBlock<'s> {
|
|||||||
pub source: &'s str,
|
pub source: &'s str,
|
||||||
pub affiliated_keywords: AffiliatedKeywords<'s>,
|
pub affiliated_keywords: AffiliatedKeywords<'s>,
|
||||||
pub contents: &'s str,
|
pub contents: &'s str,
|
||||||
|
pub post_blank: Option<&'s str>,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub type CharOffsetInLine = u16;
|
pub type CharOffsetInLine = u16;
|
||||||
@@ -59,7 +83,8 @@ pub struct ExampleBlock<'s> {
|
|||||||
pub retain_labels: RetainLabels,
|
pub retain_labels: RetainLabels,
|
||||||
pub use_labels: bool,
|
pub use_labels: bool,
|
||||||
pub label_format: Option<&'s str>,
|
pub label_format: Option<&'s str>,
|
||||||
pub contents: String,
|
pub value: &'s str,
|
||||||
|
pub post_blank: Option<&'s str>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
@@ -68,7 +93,8 @@ pub struct ExportBlock<'s> {
|
|||||||
pub affiliated_keywords: AffiliatedKeywords<'s>,
|
pub affiliated_keywords: AffiliatedKeywords<'s>,
|
||||||
pub export_type: Option<&'s str>,
|
pub export_type: Option<&'s str>,
|
||||||
pub data: Option<&'s str>,
|
pub data: Option<&'s str>,
|
||||||
pub contents: String,
|
pub value: &'s str,
|
||||||
|
pub post_blank: Option<&'s str>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
@@ -83,7 +109,8 @@ pub struct SrcBlock<'s> {
|
|||||||
pub retain_labels: RetainLabels,
|
pub retain_labels: RetainLabels,
|
||||||
pub use_labels: bool,
|
pub use_labels: bool,
|
||||||
pub label_format: Option<&'s str>,
|
pub label_format: Option<&'s str>,
|
||||||
pub contents: String,
|
pub value: &'s str,
|
||||||
|
pub post_blank: Option<&'s str>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
@@ -98,6 +125,7 @@ pub struct Clock<'s> {
|
|||||||
pub timestamp: Timestamp<'s>,
|
pub timestamp: Timestamp<'s>,
|
||||||
pub duration: Option<&'s str>,
|
pub duration: Option<&'s str>,
|
||||||
pub status: ClockStatus,
|
pub status: ClockStatus,
|
||||||
|
pub post_blank: Option<&'s str>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
@@ -105,6 +133,7 @@ pub struct DiarySexp<'s> {
|
|||||||
pub source: &'s str,
|
pub source: &'s str,
|
||||||
pub affiliated_keywords: AffiliatedKeywords<'s>,
|
pub affiliated_keywords: AffiliatedKeywords<'s>,
|
||||||
pub value: &'s str,
|
pub value: &'s str,
|
||||||
|
pub post_blank: Option<&'s str>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
@@ -113,6 +142,7 @@ pub struct Planning<'s> {
|
|||||||
pub scheduled: Option<Timestamp<'s>>,
|
pub scheduled: Option<Timestamp<'s>>,
|
||||||
pub deadline: Option<Timestamp<'s>>,
|
pub deadline: Option<Timestamp<'s>>,
|
||||||
pub closed: Option<Timestamp<'s>>,
|
pub closed: Option<Timestamp<'s>>,
|
||||||
|
pub post_blank: Option<&'s str>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
@@ -120,12 +150,14 @@ pub struct FixedWidthArea<'s> {
|
|||||||
pub source: &'s str,
|
pub source: &'s str,
|
||||||
pub affiliated_keywords: AffiliatedKeywords<'s>,
|
pub affiliated_keywords: AffiliatedKeywords<'s>,
|
||||||
pub value: Vec<&'s str>,
|
pub value: Vec<&'s str>,
|
||||||
|
pub post_blank: Option<&'s str>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct HorizontalRule<'s> {
|
pub struct HorizontalRule<'s> {
|
||||||
pub source: &'s str,
|
pub source: &'s str,
|
||||||
pub affiliated_keywords: AffiliatedKeywords<'s>,
|
pub affiliated_keywords: AffiliatedKeywords<'s>,
|
||||||
|
pub post_blank: Option<&'s str>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
@@ -134,6 +166,7 @@ pub struct Keyword<'s> {
|
|||||||
pub affiliated_keywords: AffiliatedKeywords<'s>,
|
pub affiliated_keywords: AffiliatedKeywords<'s>,
|
||||||
pub key: &'s str,
|
pub key: &'s str,
|
||||||
pub value: &'s str,
|
pub value: &'s str,
|
||||||
|
pub post_blank: Option<&'s str>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
@@ -145,6 +178,7 @@ pub struct BabelCall<'s> {
|
|||||||
pub inside_header: Option<&'s str>,
|
pub inside_header: Option<&'s str>,
|
||||||
pub arguments: Option<&'s str>,
|
pub arguments: Option<&'s str>,
|
||||||
pub end_header: Option<&'s str>,
|
pub end_header: Option<&'s str>,
|
||||||
|
pub post_blank: Option<&'s str>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
@@ -152,6 +186,7 @@ pub struct LatexEnvironment<'s> {
|
|||||||
pub source: &'s str,
|
pub source: &'s str,
|
||||||
pub affiliated_keywords: AffiliatedKeywords<'s>,
|
pub affiliated_keywords: AffiliatedKeywords<'s>,
|
||||||
pub value: &'s str,
|
pub value: &'s str,
|
||||||
|
pub post_blank: Option<&'s str>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A line number used in switches to lesser blocks.
|
/// A line number used in switches to lesser blocks.
|
||||||
@@ -169,11 +204,18 @@ impl<'s> Paragraph<'s> {
|
|||||||
/// Generate a paragraph of the passed in text with no additional properties.
|
/// Generate a paragraph of the passed in text with no additional properties.
|
||||||
///
|
///
|
||||||
/// This is used for elements that support an "empty" content like greater blocks.
|
/// This is used for elements that support an "empty" content like greater blocks.
|
||||||
pub(crate) fn of_text(input: &'s str) -> Self {
|
pub(crate) fn of_text(
|
||||||
|
source: &'s str,
|
||||||
|
body: &'s str,
|
||||||
|
contents: Option<&'s str>,
|
||||||
|
post_blank: Option<&'s str>,
|
||||||
|
) -> Self {
|
||||||
Paragraph {
|
Paragraph {
|
||||||
source: input,
|
source,
|
||||||
|
contents,
|
||||||
|
post_blank,
|
||||||
affiliated_keywords: AffiliatedKeywords::default(),
|
affiliated_keywords: AffiliatedKeywords::default(),
|
||||||
children: vec![Object::PlainText(PlainText { source: input })],
|
children: vec![Object::PlainText(PlainText { source: body })],
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -182,92 +224,280 @@ impl<'s> StandardProperties<'s> for Paragraph<'s> {
|
|||||||
fn get_source<'b>(&'b self) -> &'s str {
|
fn get_source<'b>(&'b self) -> &'s str {
|
||||||
self.source
|
self.source
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn get_contents<'b>(&'b self) -> Option<&'s str> {
|
||||||
|
self.contents
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_post_blank(&self) -> PostBlank {
|
||||||
|
self.post_blank
|
||||||
|
.map(|text| text.lines().count())
|
||||||
|
.unwrap_or(0)
|
||||||
|
.try_into()
|
||||||
|
.expect("Too much post-blank to fit into a PostBlank.")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'s> StandardProperties<'s> for TableCell<'s> {
|
impl<'s> StandardProperties<'s> for TableCell<'s> {
|
||||||
fn get_source<'b>(&'b self) -> &'s str {
|
fn get_source<'b>(&'b self) -> &'s str {
|
||||||
self.source
|
self.source
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn get_contents<'b>(&'b self) -> Option<&'s str> {
|
||||||
|
Some(self.contents)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_post_blank(&self) -> PostBlank {
|
||||||
|
0
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'s> StandardProperties<'s> for Comment<'s> {
|
impl<'s> StandardProperties<'s> for Comment<'s> {
|
||||||
fn get_source<'b>(&'b self) -> &'s str {
|
fn get_source<'b>(&'b self) -> &'s str {
|
||||||
self.source
|
self.source
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn get_contents<'b>(&'b self) -> Option<&'s str> {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_post_blank(&self) -> PostBlank {
|
||||||
|
self.post_blank
|
||||||
|
.map(|text| text.lines().count())
|
||||||
|
.unwrap_or(0)
|
||||||
|
.try_into()
|
||||||
|
.expect("Too much post-blank to fit into a PostBlank.")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'s> StandardProperties<'s> for VerseBlock<'s> {
|
impl<'s> StandardProperties<'s> for VerseBlock<'s> {
|
||||||
fn get_source<'b>(&'b self) -> &'s str {
|
fn get_source<'b>(&'b self) -> &'s str {
|
||||||
self.source
|
self.source
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn get_contents<'b>(&'b self) -> Option<&'s str> {
|
||||||
|
Some(self.contents)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_post_blank(&self) -> PostBlank {
|
||||||
|
self.post_blank
|
||||||
|
.map(|text| text.lines().count())
|
||||||
|
.unwrap_or(0)
|
||||||
|
.try_into()
|
||||||
|
.expect("Too much post-blank to fit into a PostBlank.")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
impl<'s> StandardProperties<'s> for CommentBlock<'s> {
|
impl<'s> StandardProperties<'s> for CommentBlock<'s> {
|
||||||
fn get_source<'b>(&'b self) -> &'s str {
|
fn get_source<'b>(&'b self) -> &'s str {
|
||||||
self.source
|
self.source
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn get_contents<'b>(&'b self) -> Option<&'s str> {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_post_blank(&self) -> PostBlank {
|
||||||
|
self.post_blank
|
||||||
|
.map(|text| text.lines().count())
|
||||||
|
.unwrap_or(0)
|
||||||
|
.try_into()
|
||||||
|
.expect("Too much post-blank to fit into a PostBlank.")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
impl<'s> StandardProperties<'s> for ExampleBlock<'s> {
|
impl<'s> StandardProperties<'s> for ExampleBlock<'s> {
|
||||||
fn get_source<'b>(&'b self) -> &'s str {
|
fn get_source<'b>(&'b self) -> &'s str {
|
||||||
self.source
|
self.source
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn get_contents<'b>(&'b self) -> Option<&'s str> {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_post_blank(&self) -> PostBlank {
|
||||||
|
self.post_blank
|
||||||
|
.map(|text| text.lines().count())
|
||||||
|
.unwrap_or(0)
|
||||||
|
.try_into()
|
||||||
|
.expect("Too much post-blank to fit into a PostBlank.")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
impl<'s> StandardProperties<'s> for ExportBlock<'s> {
|
impl<'s> StandardProperties<'s> for ExportBlock<'s> {
|
||||||
fn get_source<'b>(&'b self) -> &'s str {
|
fn get_source<'b>(&'b self) -> &'s str {
|
||||||
self.source
|
self.source
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn get_contents<'b>(&'b self) -> Option<&'s str> {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_post_blank(&self) -> PostBlank {
|
||||||
|
self.post_blank
|
||||||
|
.map(|text| text.lines().count())
|
||||||
|
.unwrap_or(0)
|
||||||
|
.try_into()
|
||||||
|
.expect("Too much post-blank to fit into a PostBlank.")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
impl<'s> StandardProperties<'s> for SrcBlock<'s> {
|
impl<'s> StandardProperties<'s> for SrcBlock<'s> {
|
||||||
fn get_source<'b>(&'b self) -> &'s str {
|
fn get_source<'b>(&'b self) -> &'s str {
|
||||||
self.source
|
self.source
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn get_contents<'b>(&'b self) -> Option<&'s str> {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_post_blank(&self) -> PostBlank {
|
||||||
|
self.post_blank
|
||||||
|
.map(|text| text.lines().count())
|
||||||
|
.unwrap_or(0)
|
||||||
|
.try_into()
|
||||||
|
.expect("Too much post-blank to fit into a PostBlank.")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'s> StandardProperties<'s> for Clock<'s> {
|
impl<'s> StandardProperties<'s> for Clock<'s> {
|
||||||
fn get_source<'b>(&'b self) -> &'s str {
|
fn get_source<'b>(&'b self) -> &'s str {
|
||||||
self.source
|
self.source
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn get_contents<'b>(&'b self) -> Option<&'s str> {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_post_blank(&self) -> PostBlank {
|
||||||
|
self.post_blank
|
||||||
|
.map(|text| text.lines().count())
|
||||||
|
.unwrap_or(0)
|
||||||
|
.try_into()
|
||||||
|
.expect("Too much post-blank to fit into a PostBlank.")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'s> StandardProperties<'s> for DiarySexp<'s> {
|
impl<'s> StandardProperties<'s> for DiarySexp<'s> {
|
||||||
fn get_source<'b>(&'b self) -> &'s str {
|
fn get_source<'b>(&'b self) -> &'s str {
|
||||||
self.source
|
self.source
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn get_contents<'b>(&'b self) -> Option<&'s str> {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_post_blank(&self) -> PostBlank {
|
||||||
|
self.post_blank
|
||||||
|
.map(|text| text.lines().count())
|
||||||
|
.unwrap_or(0)
|
||||||
|
.try_into()
|
||||||
|
.expect("Too much post-blank to fit into a PostBlank.")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'s> StandardProperties<'s> for Planning<'s> {
|
impl<'s> StandardProperties<'s> for Planning<'s> {
|
||||||
fn get_source<'b>(&'b self) -> &'s str {
|
fn get_source<'b>(&'b self) -> &'s str {
|
||||||
self.source
|
self.source
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn get_contents<'b>(&'b self) -> Option<&'s str> {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_post_blank(&self) -> PostBlank {
|
||||||
|
self.post_blank
|
||||||
|
.map(|text| text.lines().count())
|
||||||
|
.unwrap_or(0)
|
||||||
|
.try_into()
|
||||||
|
.expect("Too much post-blank to fit into a PostBlank.")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'s> StandardProperties<'s> for FixedWidthArea<'s> {
|
impl<'s> StandardProperties<'s> for FixedWidthArea<'s> {
|
||||||
fn get_source<'b>(&'b self) -> &'s str {
|
fn get_source<'b>(&'b self) -> &'s str {
|
||||||
self.source
|
self.source
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn get_contents<'b>(&'b self) -> Option<&'s str> {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_post_blank(&self) -> PostBlank {
|
||||||
|
self.post_blank
|
||||||
|
.map(|text| text.lines().count())
|
||||||
|
.unwrap_or(0)
|
||||||
|
.try_into()
|
||||||
|
.expect("Too much post-blank to fit into a PostBlank.")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'s> StandardProperties<'s> for HorizontalRule<'s> {
|
impl<'s> StandardProperties<'s> for HorizontalRule<'s> {
|
||||||
fn get_source<'b>(&'b self) -> &'s str {
|
fn get_source<'b>(&'b self) -> &'s str {
|
||||||
self.source
|
self.source
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn get_contents<'b>(&'b self) -> Option<&'s str> {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_post_blank(&self) -> PostBlank {
|
||||||
|
self.post_blank
|
||||||
|
.map(|text| text.lines().count())
|
||||||
|
.unwrap_or(0)
|
||||||
|
.try_into()
|
||||||
|
.expect("Too much post-blank to fit into a PostBlank.")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'s> StandardProperties<'s> for Keyword<'s> {
|
impl<'s> StandardProperties<'s> for Keyword<'s> {
|
||||||
fn get_source<'b>(&'b self) -> &'s str {
|
fn get_source<'b>(&'b self) -> &'s str {
|
||||||
self.source
|
self.source
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn get_contents<'b>(&'b self) -> Option<&'s str> {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_post_blank(&self) -> PostBlank {
|
||||||
|
self.post_blank
|
||||||
|
.map(|text| text.lines().count())
|
||||||
|
.unwrap_or(0)
|
||||||
|
.try_into()
|
||||||
|
.expect("Too much post-blank to fit into a PostBlank.")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'s> StandardProperties<'s> for BabelCall<'s> {
|
impl<'s> StandardProperties<'s> for BabelCall<'s> {
|
||||||
fn get_source<'b>(&'b self) -> &'s str {
|
fn get_source<'b>(&'b self) -> &'s str {
|
||||||
self.source
|
self.source
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn get_contents<'b>(&'b self) -> Option<&'s str> {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_post_blank(&self) -> PostBlank {
|
||||||
|
self.post_blank
|
||||||
|
.map(|text| text.lines().count())
|
||||||
|
.unwrap_or(0)
|
||||||
|
.try_into()
|
||||||
|
.expect("Too much post-blank to fit into a PostBlank.")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'s> StandardProperties<'s> for LatexEnvironment<'s> {
|
impl<'s> StandardProperties<'s> for LatexEnvironment<'s> {
|
||||||
fn get_source<'b>(&'b self) -> &'s str {
|
fn get_source<'b>(&'b self) -> &'s str {
|
||||||
self.source
|
self.source
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn get_contents<'b>(&'b self) -> Option<&'s str> {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_post_blank(&self) -> PostBlank {
|
||||||
|
self.post_blank
|
||||||
|
.map(|text| text.lines().count())
|
||||||
|
.unwrap_or(0)
|
||||||
|
.try_into()
|
||||||
|
.expect("Too much post-blank to fit into a PostBlank.")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'s> Comment<'s> {
|
impl<'s> Comment<'s> {
|
||||||
@@ -284,13 +514,14 @@ impl<'s> Comment<'s> {
|
|||||||
|
|
||||||
impl<'s> FixedWidthArea<'s> {
|
impl<'s> FixedWidthArea<'s> {
|
||||||
pub fn get_value(&self) -> String {
|
pub fn get_value(&self) -> String {
|
||||||
let final_size = self.value.iter().map(|line| line.len()).sum();
|
self.value.join("\n")
|
||||||
let mut ret = String::with_capacity(final_size);
|
}
|
||||||
for line in &self.value {
|
|
||||||
ret.push_str(line);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ret
|
impl<'s> ExampleBlock<'s> {
|
||||||
|
/// Gets the contents of the lesser block, handling the escaping of lines with leading commas.
|
||||||
|
pub fn get_value(&self) -> Cow<'s, str> {
|
||||||
|
lesser_block_content(self.value).expect("This parser should never fail.")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -301,6 +532,18 @@ impl<'s> ExportBlock<'s> {
|
|||||||
pub fn get_export_type(&self) -> Option<String> {
|
pub fn get_export_type(&self) -> Option<String> {
|
||||||
self.export_type.map(|s| s.to_uppercase())
|
self.export_type.map(|s| s.to_uppercase())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Gets the contents of the lesser block, handling the escaping of lines with leading commas.
|
||||||
|
pub fn get_value(&self) -> Cow<'s, str> {
|
||||||
|
lesser_block_content(self.value).expect("This parser should never fail.")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'s> SrcBlock<'s> {
|
||||||
|
/// Gets the contents of the lesser block, handling the escaping of lines with leading commas.
|
||||||
|
pub fn get_value(&self) -> Cow<'s, str> {
|
||||||
|
lesser_block_content(self.value).expect("This parser should never fail.")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'s> GetAffiliatedKeywords<'s> for Paragraph<'s> {
|
impl<'s> GetAffiliatedKeywords<'s> for Paragraph<'s> {
|
||||||
@@ -374,3 +617,82 @@ impl<'s> GetAffiliatedKeywords<'s> for VerseBlock<'s> {
|
|||||||
&self.affiliated_keywords
|
&self.affiliated_keywords
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
enum ContentState {
|
||||||
|
Normal,
|
||||||
|
Modified(String),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
|
||||||
|
fn lesser_block_content<'s>(input: &'s str) -> Result<Cow<'s, str>, CustomError> {
|
||||||
|
let mut state = ContentState::Normal;
|
||||||
|
let mut remaining = input;
|
||||||
|
loop {
|
||||||
|
if remaining.is_empty() {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
let (remain, (pre_escape_whitespace, line)) =
|
||||||
|
content_line(remaining).map_err(|err| match err {
|
||||||
|
nom::Err::Incomplete(_) => panic!("This parser does not support streaming."),
|
||||||
|
nom::Err::Error(e) => e,
|
||||||
|
nom::Err::Failure(e) => e,
|
||||||
|
})?;
|
||||||
|
if let Some(val) = pre_escape_whitespace {
|
||||||
|
if let ContentState::Modified(ref mut ret) = state {
|
||||||
|
ret.push_str(val);
|
||||||
|
} else {
|
||||||
|
let mut ret = String::new();
|
||||||
|
ret.push_str(get_str_until(input, remaining));
|
||||||
|
ret.push_str(val);
|
||||||
|
state = ContentState::Modified(ret);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if let ContentState::Modified(ref mut ret) = state {
|
||||||
|
ret.push_str(line);
|
||||||
|
}
|
||||||
|
remaining = remain;
|
||||||
|
}
|
||||||
|
|
||||||
|
match state {
|
||||||
|
ContentState::Normal => Ok(Cow::Borrowed(get_str_until(input, remaining))),
|
||||||
|
ContentState::Modified(ret) => Ok(Cow::Owned(ret)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
|
||||||
|
fn content_line<'s>(input: &'s str) -> Res<&'s str, (Option<&'s str>, &'s str)> {
|
||||||
|
let (remaining, pre_escape_whitespace) = opt(map(
|
||||||
|
tuple((
|
||||||
|
recognize(tuple((
|
||||||
|
space0,
|
||||||
|
many_till(
|
||||||
|
tag(","),
|
||||||
|
peek(tuple((tag(","), alt((tag("#+"), tag("*")))))),
|
||||||
|
),
|
||||||
|
))),
|
||||||
|
tag(","),
|
||||||
|
)),
|
||||||
|
|(pre_comma, _)| pre_comma,
|
||||||
|
))(input)?;
|
||||||
|
let (remaining, line_post_escape) = recognize(many_till(anychar, line_ending))(remaining)?;
|
||||||
|
Ok((remaining, (pre_escape_whitespace, line_post_escape)))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
|
||||||
|
/// Check if the child string slice is a slice of the parent string slice.
|
||||||
|
pub(crate) fn is_slice_of(parent: &str, child: &str) -> bool {
|
||||||
|
let parent_start = parent.as_ptr() as usize;
|
||||||
|
let parent_end = parent_start + parent.len();
|
||||||
|
let child_start = child.as_ptr() as usize;
|
||||||
|
let child_end = child_start + child.len();
|
||||||
|
child_start >= parent_start && child_end <= parent_end
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
|
||||||
|
fn get_str_until<'s>(parent: &'s str, child: &'s str) -> &'s str {
|
||||||
|
debug_assert!(is_slice_of(parent, child));
|
||||||
|
let parent_start = parent.as_ptr() as usize;
|
||||||
|
let child_start = child.as_ptr() as usize;
|
||||||
|
&parent[..(child_start - parent_start)]
|
||||||
|
}
|
||||||
|
|||||||
@@ -2,19 +2,18 @@ mod affiliated_keyword;
|
|||||||
mod ast_node;
|
mod ast_node;
|
||||||
mod document;
|
mod document;
|
||||||
mod element;
|
mod element;
|
||||||
mod get_standard_properties;
|
|
||||||
mod greater_element;
|
mod greater_element;
|
||||||
mod lesser_element;
|
mod lesser_element;
|
||||||
mod macros;
|
mod macros;
|
||||||
mod object;
|
mod object;
|
||||||
mod source;
|
mod remove_trailing;
|
||||||
mod standard_properties;
|
mod standard_properties;
|
||||||
mod util;
|
mod util;
|
||||||
pub use affiliated_keyword::AffiliatedKeyword;
|
pub use affiliated_keyword::AffiliatedKeyword;
|
||||||
pub use affiliated_keyword::AffiliatedKeywordValue;
|
pub use affiliated_keyword::AffiliatedKeywordValue;
|
||||||
pub use affiliated_keyword::AffiliatedKeywords;
|
pub use affiliated_keyword::AffiliatedKeywords;
|
||||||
pub use affiliated_keyword::GetAffiliatedKeywords;
|
pub use affiliated_keyword::GetAffiliatedKeywords;
|
||||||
pub(crate) use ast_node::AstNode;
|
pub use ast_node::AstNode;
|
||||||
pub use document::Document;
|
pub use document::Document;
|
||||||
pub use document::DocumentElement;
|
pub use document::DocumentElement;
|
||||||
pub use document::Heading;
|
pub use document::Heading;
|
||||||
@@ -23,7 +22,6 @@ pub use document::PriorityCookie;
|
|||||||
pub use document::Section;
|
pub use document::Section;
|
||||||
pub use document::TodoKeywordType;
|
pub use document::TodoKeywordType;
|
||||||
pub use element::Element;
|
pub use element::Element;
|
||||||
pub use get_standard_properties::GetStandardProperties;
|
|
||||||
pub use greater_element::CenterBlock;
|
pub use greater_element::CenterBlock;
|
||||||
pub use greater_element::CheckboxType;
|
pub use greater_element::CheckboxType;
|
||||||
pub use greater_element::Drawer;
|
pub use greater_element::Drawer;
|
||||||
@@ -113,5 +111,5 @@ pub use object::WarningDelay;
|
|||||||
pub use object::WarningDelayType;
|
pub use object::WarningDelayType;
|
||||||
pub use object::Year;
|
pub use object::Year;
|
||||||
pub use object::YearInner;
|
pub use object::YearInner;
|
||||||
pub(crate) use source::SetSource;
|
pub use standard_properties::PostBlank;
|
||||||
pub use standard_properties::StandardProperties;
|
pub use standard_properties::StandardProperties;
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
56
src/types/remove_trailing.rs
Normal file
56
src/types/remove_trailing.rs
Normal file
@@ -0,0 +1,56 @@
|
|||||||
|
pub(crate) trait RemoveTrailing: Iterator + Sized {
|
||||||
|
fn remove_trailing<R: Into<usize>>(self, amount_to_remove: R) -> RemoveTrailingIter<Self>;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<I> RemoveTrailing for I
|
||||||
|
where
|
||||||
|
I: Iterator,
|
||||||
|
{
|
||||||
|
fn remove_trailing<R: Into<usize>>(self, amount_to_remove: R) -> RemoveTrailingIter<Self> {
|
||||||
|
RemoveTrailingIter {
|
||||||
|
inner: self,
|
||||||
|
buffer: Vec::new(),
|
||||||
|
next_to_pop: 0,
|
||||||
|
amount_to_remove: amount_to_remove.into(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) struct RemoveTrailingIter<I: Iterator> {
|
||||||
|
inner: I,
|
||||||
|
buffer: Vec<I::Item>,
|
||||||
|
next_to_pop: usize,
|
||||||
|
amount_to_remove: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<I: Iterator> Iterator for RemoveTrailingIter<I> {
|
||||||
|
type Item = I::Item;
|
||||||
|
|
||||||
|
fn next(&mut self) -> Option<Self::Item> {
|
||||||
|
if self.buffer.len() < self.amount_to_remove {
|
||||||
|
self.buffer.reserve_exact(self.amount_to_remove);
|
||||||
|
}
|
||||||
|
while self.buffer.len() < self.amount_to_remove {
|
||||||
|
if let Some(elem) = self.inner.next() {
|
||||||
|
self.buffer.push(elem);
|
||||||
|
} else {
|
||||||
|
// The inner was smaller than amount_to_remove, so never return anything.
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let new_value = self.inner.next();
|
||||||
|
if self.amount_to_remove == 0 {
|
||||||
|
return new_value;
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(new_value) = new_value {
|
||||||
|
let ret = std::mem::replace(&mut self.buffer[self.next_to_pop], new_value);
|
||||||
|
self.next_to_pop = (self.next_to_pop + 1) % self.amount_to_remove;
|
||||||
|
Some(ret)
|
||||||
|
} else {
|
||||||
|
// We have exactly the amount in the buffer than we wanted to cut off, so stop returning values.
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,3 +0,0 @@
|
|||||||
pub(crate) trait SetSource<'s> {
|
|
||||||
fn set_source(&mut self, source: &'s str);
|
|
||||||
}
|
|
||||||
@@ -5,10 +5,15 @@ pub trait StandardProperties<'s> {
|
|||||||
/// This corresponds to :begin to :end in upstream org-mode's standard properties.
|
/// This corresponds to :begin to :end in upstream org-mode's standard properties.
|
||||||
fn get_source<'b>(&'b self) -> &'s str;
|
fn get_source<'b>(&'b self) -> &'s str;
|
||||||
|
|
||||||
// Get the slice of the AST node's contents.
|
/// Get the slice of the AST node's contents.
|
||||||
//
|
///
|
||||||
// This corresponds to :contents-begin to :contents-end
|
/// This corresponds to :contents-begin to :contents-end
|
||||||
// fn get_contents(&'s self) -> &'s str;
|
fn get_contents<'b>(&'b self) -> Option<&'s str>;
|
||||||
|
|
||||||
|
/// Get the ast node's post-blank.
|
||||||
|
///
|
||||||
|
/// For objects this is a count of the characters of whitespace after the object. For elements this is a count of the line breaks following an element.
|
||||||
|
fn get_post_blank(&self) -> PostBlank;
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Write some debugging code to alert when any of the unknown fields below are non-nil in our test data so we can see what these fields represent.
|
// TODO: Write some debugging code to alert when any of the unknown fields below are non-nil in our test data so we can see what these fields represent.
|
||||||
@@ -56,3 +61,5 @@ pub trait StandardProperties<'s> {
|
|||||||
// X :parent - Some weird numeric reference to the containing object. Since we output a tree structure, I do not see any value in including this, especially considering the back-references would be a nightmare in rust.
|
// X :parent - Some weird numeric reference to the containing object. Since we output a tree structure, I do not see any value in including this, especially considering the back-references would be a nightmare in rust.
|
||||||
|
|
||||||
// Special case: Plain text. Plain text counts :begin and :end from the start of the text (so :begin is always 0 AFAICT) and instead of including the full set of standard properties, it only includes :begin, :end, and :parent.
|
// Special case: Plain text. Plain text counts :begin and :end from the start of the text (so :begin is always 0 AFAICT) and instead of including the full set of standard properties, it only includes :begin, :end, and :parent.
|
||||||
|
|
||||||
|
pub type PostBlank = u8;
|
||||||
|
|||||||
@@ -2,28 +2,53 @@ use std::path::Path;
|
|||||||
|
|
||||||
use tokio::process::Command;
|
use tokio::process::Command;
|
||||||
|
|
||||||
use crate::context::HeadlineLevelFilter;
|
|
||||||
use crate::settings::GlobalSettings;
|
use crate::settings::GlobalSettings;
|
||||||
|
use crate::settings::HeadlineLevelFilter;
|
||||||
|
|
||||||
/// Generate elisp to configure org-mode parsing settings
|
pub async fn print_versions() -> Result<(), Box<dyn std::error::Error>> {
|
||||||
///
|
eprintln!("Using emacs version: {}", get_emacs_version().await?.trim());
|
||||||
/// Currently only org-list-allow-alphabetical is supported.
|
eprintln!(
|
||||||
fn global_settings_elisp(global_settings: &GlobalSettings) -> String {
|
"Using org-mode version: {}",
|
||||||
// This string concatenation is wildly inefficient but its only called in tests 🤷.
|
get_org_mode_version().await?.trim()
|
||||||
let mut ret = "".to_owned();
|
);
|
||||||
if global_settings.list_allow_alphabetical {
|
Ok(())
|
||||||
ret += "(setq org-list-allow-alphabetical t)\n"
|
|
||||||
}
|
}
|
||||||
if global_settings.tab_width != crate::settings::DEFAULT_TAB_WIDTH {
|
|
||||||
ret += format!("(setq-default tab-width {})", global_settings.tab_width).as_str();
|
pub(crate) async fn get_emacs_version() -> Result<String, Box<dyn std::error::Error>> {
|
||||||
|
let elisp_script = r#"(progn
|
||||||
|
(message "%s" (version))
|
||||||
|
)"#;
|
||||||
|
let mut cmd = Command::new("emacs");
|
||||||
|
let cmd = cmd
|
||||||
|
.arg("-q")
|
||||||
|
.arg("--no-site-file")
|
||||||
|
.arg("--no-splash")
|
||||||
|
.arg("--batch")
|
||||||
|
.arg("--eval")
|
||||||
|
.arg(elisp_script);
|
||||||
|
|
||||||
|
let out = cmd.output().await?;
|
||||||
|
out.status.exit_ok()?;
|
||||||
|
Ok(String::from_utf8(out.stderr)?)
|
||||||
}
|
}
|
||||||
if global_settings.odd_levels_only != HeadlineLevelFilter::default() {
|
|
||||||
ret += match global_settings.odd_levels_only {
|
pub(crate) async fn get_org_mode_version() -> Result<String, Box<dyn std::error::Error>> {
|
||||||
HeadlineLevelFilter::Odd => "(setq org-odd-levels-only t)\n",
|
let elisp_script = r#"(progn
|
||||||
HeadlineLevelFilter::OddEven => "(setq org-odd-levels-only nil)\n",
|
(org-mode)
|
||||||
};
|
(message "%s" (org-version nil t nil))
|
||||||
}
|
)"#;
|
||||||
ret
|
let mut cmd = Command::new("emacs");
|
||||||
|
let cmd = cmd
|
||||||
|
.arg("-q")
|
||||||
|
.arg("--no-site-file")
|
||||||
|
.arg("--no-splash")
|
||||||
|
.arg("--batch")
|
||||||
|
.arg("--eval")
|
||||||
|
.arg(elisp_script);
|
||||||
|
|
||||||
|
let out = cmd.output().await?;
|
||||||
|
out.status.exit_ok()?;
|
||||||
|
Ok(String::from_utf8(out.stderr)?)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) async fn emacs_parse_anonymous_org_document<'g, 's, C>(
|
pub(crate) async fn emacs_parse_anonymous_org_document<'g, 's, C>(
|
||||||
@@ -144,39 +169,23 @@ where
|
|||||||
output
|
output
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn get_emacs_version() -> Result<String, Box<dyn std::error::Error>> {
|
/// Generate elisp to configure org-mode parsing settings
|
||||||
let elisp_script = r#"(progn
|
///
|
||||||
(message "%s" (version))
|
/// Currently only org-list-allow-alphabetical is supported.
|
||||||
)"#;
|
fn global_settings_elisp(global_settings: &GlobalSettings) -> String {
|
||||||
let mut cmd = Command::new("emacs");
|
// This string concatenation is wildly inefficient but its only called in tests 🤷.
|
||||||
let cmd = cmd
|
let mut ret = "".to_owned();
|
||||||
.arg("-q")
|
if global_settings.list_allow_alphabetical {
|
||||||
.arg("--no-site-file")
|
ret += "(setq org-list-allow-alphabetical t)\n"
|
||||||
.arg("--no-splash")
|
|
||||||
.arg("--batch")
|
|
||||||
.arg("--eval")
|
|
||||||
.arg(elisp_script);
|
|
||||||
|
|
||||||
let out = cmd.output().await?;
|
|
||||||
out.status.exit_ok()?;
|
|
||||||
Ok(String::from_utf8(out.stderr)?)
|
|
||||||
}
|
}
|
||||||
|
if global_settings.tab_width != crate::settings::DEFAULT_TAB_WIDTH {
|
||||||
pub async fn get_org_mode_version() -> Result<String, Box<dyn std::error::Error>> {
|
ret += format!("(setq-default tab-width {})", global_settings.tab_width).as_str();
|
||||||
let elisp_script = r#"(progn
|
}
|
||||||
(org-mode)
|
if global_settings.odd_levels_only != HeadlineLevelFilter::default() {
|
||||||
(message "%s" (org-version nil t nil))
|
ret += match global_settings.odd_levels_only {
|
||||||
)"#;
|
HeadlineLevelFilter::Odd => "(setq org-odd-levels-only t)\n",
|
||||||
let mut cmd = Command::new("emacs");
|
HeadlineLevelFilter::OddEven => "(setq org-odd-levels-only nil)\n",
|
||||||
let cmd = cmd
|
};
|
||||||
.arg("-q")
|
}
|
||||||
.arg("--no-site-file")
|
ret
|
||||||
.arg("--no-splash")
|
|
||||||
.arg("--batch")
|
|
||||||
.arg("--eval")
|
|
||||||
.arg(elisp_script);
|
|
||||||
|
|
||||||
let out = cmd.output().await?;
|
|
||||||
out.status.exit_ok()?;
|
|
||||||
Ok(String::from_utf8(out.stderr)?)
|
|
||||||
}
|
}
|
||||||
14
src/util/elisp/mod.rs
Normal file
14
src/util/elisp/mod.rs
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
mod sexp;
|
||||||
|
mod util;
|
||||||
|
|
||||||
|
pub use sexp::sexp;
|
||||||
|
pub(crate) use sexp::unquote;
|
||||||
|
#[cfg(feature = "wasm_test")]
|
||||||
|
pub(crate) use sexp::TextWithProperties;
|
||||||
|
pub use sexp::Token;
|
||||||
|
#[cfg(feature = "compare")]
|
||||||
|
pub(crate) use util::get_emacs_standard_properties;
|
||||||
|
#[cfg(feature = "wasm_test")]
|
||||||
|
pub(crate) use util::maybe_token_to_usize;
|
||||||
|
#[cfg(feature = "wasm_test")]
|
||||||
|
pub(crate) use util::EmacsStandardProperties;
|
||||||
@@ -1,3 +1,4 @@
|
|||||||
|
use std::borrow::Cow;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
|
||||||
use nom::branch::alt;
|
use nom::branch::alt;
|
||||||
@@ -36,12 +37,6 @@ pub struct TextWithProperties<'s> {
|
|||||||
pub(crate) properties: Vec<Token<'s>>,
|
pub(crate) properties: Vec<Token<'s>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
enum ParseState {
|
|
||||||
Normal,
|
|
||||||
Escape,
|
|
||||||
Octal(Vec<u8>),
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'s> Token<'s> {
|
impl<'s> Token<'s> {
|
||||||
pub(crate) fn as_vector<'p>(
|
pub(crate) fn as_vector<'p>(
|
||||||
&'p self,
|
&'p self,
|
||||||
@@ -66,6 +61,7 @@ impl<'s> Token<'s> {
|
|||||||
}?)
|
}?)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "compare")]
|
||||||
pub(crate) fn as_text<'p>(
|
pub(crate) fn as_text<'p>(
|
||||||
&'p self,
|
&'p self,
|
||||||
) -> Result<&'p TextWithProperties<'s>, Box<dyn std::error::Error>> {
|
) -> Result<&'p TextWithProperties<'s>, Box<dyn std::error::Error>> {
|
||||||
@@ -117,8 +113,27 @@ fn get_consumed<'s>(input: &'s str, remaining: &'s str) -> &'s str {
|
|||||||
&input[..offset]
|
&input[..offset]
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn unquote(text: &str) -> Result<String, Box<dyn std::error::Error>> {
|
#[derive(Debug)]
|
||||||
let mut out: Vec<u8> = Vec::with_capacity(text.len());
|
enum UnquoteState {
|
||||||
|
Normal,
|
||||||
|
Escape,
|
||||||
|
HasEscape {
|
||||||
|
out: Vec<u8>,
|
||||||
|
},
|
||||||
|
HasEscapeEscape {
|
||||||
|
out: Vec<u8>,
|
||||||
|
},
|
||||||
|
Octal {
|
||||||
|
octal_begin_offset: usize,
|
||||||
|
octal: Vec<u8>,
|
||||||
|
},
|
||||||
|
HasEscapeOctal {
|
||||||
|
out: Vec<u8>,
|
||||||
|
octal: Vec<u8>,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn unquote(text: &str) -> Result<Cow<'_, str>, Box<dyn std::error::Error>> {
|
||||||
if !text.starts_with('"') {
|
if !text.starts_with('"') {
|
||||||
return Err("Quoted text does not start with quote.".into());
|
return Err("Quoted text does not start with quote.".into());
|
||||||
}
|
}
|
||||||
@@ -126,54 +141,143 @@ pub(crate) fn unquote(text: &str) -> Result<String, Box<dyn std::error::Error>>
|
|||||||
return Err("Quoted text does not end with quote.".into());
|
return Err("Quoted text does not end with quote.".into());
|
||||||
}
|
}
|
||||||
let interior_text = &text[1..(text.len() - 1)];
|
let interior_text = &text[1..(text.len() - 1)];
|
||||||
let mut state = ParseState::Normal;
|
let mut state = UnquoteState::Normal;
|
||||||
for current_char in interior_text.bytes() {
|
for (offset, current_char) in interior_text.bytes().enumerate() {
|
||||||
// Check to see if octal finished
|
// Check to see if octal finished
|
||||||
state = match (state, current_char) {
|
state = match (state, current_char) {
|
||||||
(ParseState::Octal(octal), b'0'..=b'7') if octal.len() < MAX_OCTAL_LENGTH => {
|
(
|
||||||
ParseState::Octal(octal)
|
UnquoteState::Octal {
|
||||||
|
octal_begin_offset,
|
||||||
|
octal,
|
||||||
|
},
|
||||||
|
b'0'..=b'7',
|
||||||
|
) if octal.len() < MAX_OCTAL_LENGTH => UnquoteState::Octal {
|
||||||
|
octal_begin_offset,
|
||||||
|
octal,
|
||||||
|
},
|
||||||
|
(
|
||||||
|
UnquoteState::Octal {
|
||||||
|
octal_begin_offset,
|
||||||
|
octal,
|
||||||
|
},
|
||||||
|
_,
|
||||||
|
) => {
|
||||||
|
let octal_number_string = String::from_utf8(octal)?;
|
||||||
|
let decoded_byte = u8::from_str_radix(&octal_number_string, 8)?;
|
||||||
|
let mut out: Vec<u8> = Vec::with_capacity(interior_text.len());
|
||||||
|
out.extend_from_slice(&interior_text.as_bytes()[..octal_begin_offset]);
|
||||||
|
out.push(decoded_byte);
|
||||||
|
UnquoteState::HasEscape { out }
|
||||||
}
|
}
|
||||||
(ParseState::Octal(octal), _) => {
|
(UnquoteState::HasEscapeOctal { out, octal }, b'0'..=b'7')
|
||||||
|
if octal.len() < MAX_OCTAL_LENGTH =>
|
||||||
|
{
|
||||||
|
UnquoteState::HasEscapeOctal { out, octal }
|
||||||
|
}
|
||||||
|
(UnquoteState::HasEscapeOctal { mut out, octal }, _) => {
|
||||||
let octal_number_string = String::from_utf8(octal)?;
|
let octal_number_string = String::from_utf8(octal)?;
|
||||||
let decoded_byte = u8::from_str_radix(&octal_number_string, 8)?;
|
let decoded_byte = u8::from_str_radix(&octal_number_string, 8)?;
|
||||||
out.push(decoded_byte);
|
out.push(decoded_byte);
|
||||||
ParseState::Normal
|
UnquoteState::HasEscape { out }
|
||||||
}
|
}
|
||||||
(state, _) => state,
|
(state, _) => state,
|
||||||
};
|
};
|
||||||
|
|
||||||
state = match (state, current_char) {
|
state = match (state, current_char) {
|
||||||
(ParseState::Normal, b'\\') => ParseState::Escape,
|
(UnquoteState::Normal, b'\\') => UnquoteState::Escape,
|
||||||
(ParseState::Normal, _) => {
|
(UnquoteState::Normal, _) => UnquoteState::Normal,
|
||||||
|
(UnquoteState::HasEscape { out }, b'\\') => UnquoteState::HasEscapeEscape { out },
|
||||||
|
(UnquoteState::HasEscape { mut out }, _) => {
|
||||||
out.push(current_char);
|
out.push(current_char);
|
||||||
ParseState::Normal
|
UnquoteState::HasEscape { out }
|
||||||
}
|
}
|
||||||
(ParseState::Escape, b'n') => {
|
(UnquoteState::Escape, b'n') => {
|
||||||
|
let mut out: Vec<u8> = Vec::with_capacity(interior_text.len());
|
||||||
|
// Subtract 1 from offset to account for backslash.
|
||||||
|
out.extend_from_slice(&interior_text.as_bytes()[..(offset - 1)]);
|
||||||
out.push(b'\n');
|
out.push(b'\n');
|
||||||
ParseState::Normal
|
UnquoteState::HasEscape { out }
|
||||||
}
|
}
|
||||||
(ParseState::Escape, b'\\') => {
|
(UnquoteState::HasEscapeEscape { mut out }, b'n') => {
|
||||||
|
out.push(b'\n');
|
||||||
|
UnquoteState::HasEscape { out }
|
||||||
|
}
|
||||||
|
(UnquoteState::Escape, b'\\') => {
|
||||||
|
let mut out: Vec<u8> = Vec::with_capacity(interior_text.len());
|
||||||
|
// Subtract 1 from offset to account for backslash.
|
||||||
|
out.extend_from_slice(&interior_text.as_bytes()[..(offset - 1)]);
|
||||||
out.push(b'\\');
|
out.push(b'\\');
|
||||||
ParseState::Normal
|
UnquoteState::HasEscape { out }
|
||||||
}
|
}
|
||||||
(ParseState::Escape, b'"') => {
|
(UnquoteState::HasEscapeEscape { mut out }, b'\\') => {
|
||||||
|
out.push(b'\\');
|
||||||
|
UnquoteState::HasEscape { out }
|
||||||
|
}
|
||||||
|
(UnquoteState::Escape, b'"') => {
|
||||||
|
let mut out: Vec<u8> = Vec::with_capacity(interior_text.len());
|
||||||
|
// Subtract 1 from offset to account for backslash.
|
||||||
|
out.extend_from_slice(&interior_text.as_bytes()[..(offset - 1)]);
|
||||||
out.push(b'"');
|
out.push(b'"');
|
||||||
ParseState::Normal
|
UnquoteState::HasEscape { out }
|
||||||
}
|
}
|
||||||
(ParseState::Escape, b'0'..=b'7') => {
|
(UnquoteState::HasEscapeEscape { mut out }, b'"') => {
|
||||||
|
out.push(b'"');
|
||||||
|
UnquoteState::HasEscape { out }
|
||||||
|
}
|
||||||
|
(UnquoteState::Escape, b'0'..=b'7') => {
|
||||||
let mut octal = Vec::with_capacity(MAX_OCTAL_LENGTH);
|
let mut octal = Vec::with_capacity(MAX_OCTAL_LENGTH);
|
||||||
octal.push(current_char);
|
octal.push(current_char);
|
||||||
ParseState::Octal(octal)
|
// Substract 1 from offset to account for backslash
|
||||||
|
UnquoteState::Octal {
|
||||||
|
octal_begin_offset: offset - 1,
|
||||||
|
octal,
|
||||||
}
|
}
|
||||||
(ParseState::Octal(mut octal), b'0'..=b'7') => {
|
}
|
||||||
|
(UnquoteState::HasEscapeEscape { out }, b'0'..=b'7') => {
|
||||||
|
let mut octal = Vec::with_capacity(MAX_OCTAL_LENGTH);
|
||||||
octal.push(current_char);
|
octal.push(current_char);
|
||||||
ParseState::Octal(octal)
|
// Substract 1 from offset to account for backslash
|
||||||
|
UnquoteState::HasEscapeOctal { out, octal }
|
||||||
}
|
}
|
||||||
_ => panic!("Invalid state unquoting string."),
|
(
|
||||||
|
UnquoteState::Octal {
|
||||||
|
octal_begin_offset,
|
||||||
|
mut octal,
|
||||||
|
},
|
||||||
|
b'0'..=b'7',
|
||||||
|
) => {
|
||||||
|
octal.push(current_char);
|
||||||
|
UnquoteState::Octal {
|
||||||
|
octal_begin_offset,
|
||||||
|
octal,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
(UnquoteState::HasEscapeOctal { out, mut octal }, b'0'..=b'7') => {
|
||||||
|
octal.push(current_char);
|
||||||
|
UnquoteState::HasEscapeOctal { out, octal }
|
||||||
|
}
|
||||||
|
(state, _) => panic!(
|
||||||
|
"Invalid state unquoting string: {:?} | {} | {:?}",
|
||||||
|
state, offset, interior_text
|
||||||
|
),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(String::from_utf8(out)?)
|
match state {
|
||||||
|
UnquoteState::Normal | UnquoteState::Escape | UnquoteState::Octal { .. } => {
|
||||||
|
Ok(Cow::Borrowed(interior_text))
|
||||||
|
}
|
||||||
|
UnquoteState::HasEscape { out } => Ok(Cow::Owned(String::from_utf8(out)?)),
|
||||||
|
UnquoteState::HasEscapeEscape { mut out } => {
|
||||||
|
out.push(b'\\');
|
||||||
|
Ok(Cow::Owned(String::from_utf8(out)?))
|
||||||
|
}
|
||||||
|
UnquoteState::HasEscapeOctal { mut out, octal } => {
|
||||||
|
out.push(b'\\');
|
||||||
|
out.extend(octal);
|
||||||
|
Ok(Cow::Owned(String::from_utf8(out)?))
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
|
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
|
||||||
76
src/util/elisp/util.rs
Normal file
76
src/util/elisp/util.rs
Normal file
@@ -0,0 +1,76 @@
|
|||||||
|
use super::Token;
|
||||||
|
|
||||||
|
pub(crate) fn maybe_token_to_usize(
|
||||||
|
token: Option<&Token<'_>>,
|
||||||
|
) -> Result<Option<usize>, Box<dyn std::error::Error>> {
|
||||||
|
Ok(token
|
||||||
|
.map(|token| token.as_atom())
|
||||||
|
.map_or(Ok(None), |r| r.map(Some))?
|
||||||
|
.and_then(|val| {
|
||||||
|
if val == "nil" {
|
||||||
|
None
|
||||||
|
} else {
|
||||||
|
Some(val.parse::<usize>())
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.map_or(Ok(None), |r| r.map(Some))?)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) struct EmacsStandardProperties {
|
||||||
|
pub(crate) begin: Option<usize>,
|
||||||
|
#[allow(dead_code)]
|
||||||
|
pub(crate) post_affiliated: Option<usize>,
|
||||||
|
#[allow(dead_code)]
|
||||||
|
pub(crate) contents_begin: Option<usize>,
|
||||||
|
#[allow(dead_code)]
|
||||||
|
pub(crate) contents_end: Option<usize>,
|
||||||
|
pub(crate) end: Option<usize>,
|
||||||
|
#[allow(dead_code)]
|
||||||
|
pub(crate) post_blank: Option<usize>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "compare")]
|
||||||
|
pub(crate) fn get_emacs_standard_properties(
|
||||||
|
emacs: &Token<'_>,
|
||||||
|
) -> Result<EmacsStandardProperties, Box<dyn std::error::Error>> {
|
||||||
|
let children = emacs.as_list()?;
|
||||||
|
let attributes_child = children.get(1).ok_or("Should have an attributes child.")?;
|
||||||
|
let attributes_map = attributes_child.as_map()?;
|
||||||
|
let standard_properties = attributes_map.get(":standard-properties");
|
||||||
|
Ok(if standard_properties.is_some() {
|
||||||
|
let mut std_props = standard_properties
|
||||||
|
.expect("if statement proves its Some")
|
||||||
|
.as_vector()?
|
||||||
|
.iter();
|
||||||
|
let begin = maybe_token_to_usize(std_props.next())?;
|
||||||
|
let post_affiliated = maybe_token_to_usize(std_props.next())?;
|
||||||
|
let contents_begin = maybe_token_to_usize(std_props.next())?;
|
||||||
|
let contents_end = maybe_token_to_usize(std_props.next())?;
|
||||||
|
let end = maybe_token_to_usize(std_props.next())?;
|
||||||
|
let post_blank = maybe_token_to_usize(std_props.next())?;
|
||||||
|
EmacsStandardProperties {
|
||||||
|
begin,
|
||||||
|
post_affiliated,
|
||||||
|
contents_begin,
|
||||||
|
contents_end,
|
||||||
|
end,
|
||||||
|
post_blank,
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
let begin = maybe_token_to_usize(attributes_map.get(":begin").copied())?;
|
||||||
|
let end = maybe_token_to_usize(attributes_map.get(":end").copied())?;
|
||||||
|
let contents_begin = maybe_token_to_usize(attributes_map.get(":contents-begin").copied())?;
|
||||||
|
let contents_end = maybe_token_to_usize(attributes_map.get(":contents-end").copied())?;
|
||||||
|
let post_blank = maybe_token_to_usize(attributes_map.get(":post-blank").copied())?;
|
||||||
|
let post_affiliated =
|
||||||
|
maybe_token_to_usize(attributes_map.get(":post-affiliated").copied())?;
|
||||||
|
EmacsStandardProperties {
|
||||||
|
begin,
|
||||||
|
post_affiliated,
|
||||||
|
contents_begin,
|
||||||
|
contents_end,
|
||||||
|
end,
|
||||||
|
post_blank,
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
8
src/util/mod.rs
Normal file
8
src/util/mod.rs
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
#[cfg(any(feature = "compare", feature = "wasm_test"))]
|
||||||
|
pub mod cli;
|
||||||
|
#[cfg(any(feature = "compare", feature = "wasm_test"))]
|
||||||
|
pub mod elisp;
|
||||||
|
#[cfg(any(feature = "compare", feature = "wasm", feature = "wasm_test"))]
|
||||||
|
pub mod elisp_fact;
|
||||||
|
#[cfg(any(feature = "compare", feature = "wasm_test"))]
|
||||||
|
pub mod terminal;
|
||||||
42
src/util/terminal.rs
Normal file
42
src/util/terminal.rs
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
use std::borrow::Cow;
|
||||||
|
|
||||||
|
fn should_use_color() -> bool {
|
||||||
|
!std::env::var("NO_COLOR").is_ok_and(|val| !val.is_empty())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn foreground_color(red: u8, green: u8, blue: u8) -> Cow<'static, str> {
|
||||||
|
if should_use_color() {
|
||||||
|
format!(
|
||||||
|
"\x1b[38;2;{red};{green};{blue}m",
|
||||||
|
red = red,
|
||||||
|
green = green,
|
||||||
|
blue = blue
|
||||||
|
)
|
||||||
|
.into()
|
||||||
|
} else {
|
||||||
|
Cow::from("")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(dead_code)]
|
||||||
|
pub(crate) fn background_color(red: u8, green: u8, blue: u8) -> Cow<'static, str> {
|
||||||
|
if should_use_color() {
|
||||||
|
format!(
|
||||||
|
"\x1b[48;2;{red};{green};{blue}m",
|
||||||
|
red = red,
|
||||||
|
green = green,
|
||||||
|
blue = blue
|
||||||
|
)
|
||||||
|
.into()
|
||||||
|
} else {
|
||||||
|
Cow::from("")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn reset_color() -> &'static str {
|
||||||
|
if should_use_color() {
|
||||||
|
"\x1b[0m"
|
||||||
|
} else {
|
||||||
|
""
|
||||||
|
}
|
||||||
|
}
|
||||||
95
src/wasm/additional_property.rs
Normal file
95
src/wasm/additional_property.rs
Normal file
@@ -0,0 +1,95 @@
|
|||||||
|
use std::collections::HashMap;
|
||||||
|
|
||||||
|
use serde::Deserialize;
|
||||||
|
use serde::Serialize;
|
||||||
|
|
||||||
|
use super::macros::to_wasm;
|
||||||
|
use super::to_wasm::ToWasm;
|
||||||
|
use super::WasmAstNode;
|
||||||
|
use crate::types::AffiliatedKeywordValue;
|
||||||
|
use crate::types::AffiliatedKeywords;
|
||||||
|
|
||||||
|
#[derive(Debug, Serialize, Deserialize)]
|
||||||
|
#[serde(untagged)]
|
||||||
|
pub enum AdditionalPropertyValue {
|
||||||
|
SingleString(String),
|
||||||
|
ListOfStrings(Vec<String>),
|
||||||
|
OptionalPair {
|
||||||
|
optval: Option<String>,
|
||||||
|
val: String,
|
||||||
|
},
|
||||||
|
ObjectTree {
|
||||||
|
#[serde(rename = "object-tree")]
|
||||||
|
object_tree: Vec<(Option<Vec<WasmAstNode>>, Vec<WasmAstNode>)>,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Serialize, Deserialize, Default)]
|
||||||
|
pub struct AdditionalProperties {
|
||||||
|
#[serde(flatten)]
|
||||||
|
pub(crate) properties: HashMap<String, AdditionalPropertyValue>,
|
||||||
|
}
|
||||||
|
|
||||||
|
to_wasm!(
|
||||||
|
AdditionalProperties,
|
||||||
|
AffiliatedKeywords<'s>,
|
||||||
|
original,
|
||||||
|
wasm_context,
|
||||||
|
{
|
||||||
|
let mut additional_properties = AdditionalProperties::default();
|
||||||
|
for (name, val) in original.keywords.iter() {
|
||||||
|
let converted_val = match val {
|
||||||
|
AffiliatedKeywordValue::SingleString(val) => {
|
||||||
|
AdditionalPropertyValue::SingleString((*val).to_owned())
|
||||||
|
}
|
||||||
|
AffiliatedKeywordValue::ListOfStrings(val) => {
|
||||||
|
AdditionalPropertyValue::ListOfStrings(
|
||||||
|
val.iter().map(|s| (*s).to_owned()).collect(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
AffiliatedKeywordValue::OptionalPair { optval, val } => {
|
||||||
|
AdditionalPropertyValue::OptionalPair {
|
||||||
|
optval: optval.map(|s| (*s).to_owned()),
|
||||||
|
val: (*val).to_owned(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
AffiliatedKeywordValue::ObjectTree(val) => {
|
||||||
|
let mut ret = Vec::with_capacity(val.len());
|
||||||
|
|
||||||
|
for (optval, value) in val {
|
||||||
|
let converted_optval = if let Some(optval) = optval {
|
||||||
|
Some(
|
||||||
|
optval
|
||||||
|
.iter()
|
||||||
|
.map(|child| {
|
||||||
|
child
|
||||||
|
.to_wasm(wasm_context.clone())
|
||||||
|
.map(Into::<WasmAstNode>::into)
|
||||||
|
})
|
||||||
|
.collect::<Result<Vec<_>, _>>()?,
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
};
|
||||||
|
let converted_value = value
|
||||||
|
.iter()
|
||||||
|
.map(|child| {
|
||||||
|
child
|
||||||
|
.to_wasm(wasm_context.clone())
|
||||||
|
.map(Into::<WasmAstNode>::into)
|
||||||
|
})
|
||||||
|
.collect::<Result<Vec<_>, _>>()?;
|
||||||
|
|
||||||
|
ret.push((converted_optval, converted_value));
|
||||||
|
}
|
||||||
|
|
||||||
|
AdditionalPropertyValue::ObjectTree { object_tree: ret }
|
||||||
|
}
|
||||||
|
};
|
||||||
|
additional_properties
|
||||||
|
.properties
|
||||||
|
.insert(name.clone(), converted_val);
|
||||||
|
}
|
||||||
|
Ok(additional_properties)
|
||||||
|
}
|
||||||
|
);
|
||||||
54
src/wasm/angle_link.rs
Normal file
54
src/wasm/angle_link.rs
Normal file
@@ -0,0 +1,54 @@
|
|||||||
|
use std::borrow::Cow;
|
||||||
|
|
||||||
|
use serde::Deserialize;
|
||||||
|
use serde::Serialize;
|
||||||
|
|
||||||
|
use super::ast_node::WasmAstNode;
|
||||||
|
use super::macros::to_wasm;
|
||||||
|
use super::to_wasm::ToWasm;
|
||||||
|
use crate::types::AngleLink;
|
||||||
|
use crate::types::LinkType;
|
||||||
|
use crate::util::elisp_fact::ElispFact;
|
||||||
|
use crate::wasm::to_wasm::ToWasmStandardProperties;
|
||||||
|
|
||||||
|
#[derive(Debug, Serialize, Deserialize)]
|
||||||
|
#[serde(tag = "format")]
|
||||||
|
#[serde(rename = "angle")]
|
||||||
|
pub struct WasmAngleLink {
|
||||||
|
#[serde(rename = "type")]
|
||||||
|
pub(crate) link_type: String,
|
||||||
|
pub(crate) path: String,
|
||||||
|
#[serde(rename = "raw-link")]
|
||||||
|
pub(crate) raw_link: String,
|
||||||
|
pub(crate) application: Option<String>,
|
||||||
|
#[serde(rename = "search-option")]
|
||||||
|
pub(crate) search_option: Option<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
to_wasm!(
|
||||||
|
WasmAngleLink,
|
||||||
|
AngleLink<'s>,
|
||||||
|
original,
|
||||||
|
wasm_context,
|
||||||
|
{ WasmAstNode::AngleLink(original) },
|
||||||
|
{ "link".into() },
|
||||||
|
{
|
||||||
|
Ok((
|
||||||
|
Vec::new(),
|
||||||
|
WasmAngleLink {
|
||||||
|
link_type: match &original.link_type {
|
||||||
|
LinkType::File => "file".to_owned(),
|
||||||
|
LinkType::Protocol(protocol) => protocol.clone().into_owned(),
|
||||||
|
LinkType::Id => "id".to_owned(),
|
||||||
|
LinkType::CustomId => "custom-id".to_owned(),
|
||||||
|
LinkType::CodeRef => "coderef".to_owned(),
|
||||||
|
LinkType::Fuzzy => "fuzzy".to_owned(),
|
||||||
|
},
|
||||||
|
path: original.get_path().into_owned(),
|
||||||
|
raw_link: original.raw_link.to_owned(),
|
||||||
|
application: original.application.map(|c| c.to_owned()),
|
||||||
|
search_option: original.get_search_option().map(Cow::into_owned),
|
||||||
|
},
|
||||||
|
))
|
||||||
|
}
|
||||||
|
);
|
||||||
142
src/wasm/ast_node.rs
Normal file
142
src/wasm/ast_node.rs
Normal file
@@ -0,0 +1,142 @@
|
|||||||
|
use serde::Deserialize;
|
||||||
|
use serde::Serialize;
|
||||||
|
|
||||||
|
use super::angle_link::WasmAngleLink;
|
||||||
|
use super::babel_call::WasmBabelCall;
|
||||||
|
use super::bold::WasmBold;
|
||||||
|
use super::center_block::WasmCenterBlock;
|
||||||
|
use super::citation::WasmCitation;
|
||||||
|
use super::citation_reference::WasmCitationReference;
|
||||||
|
use super::clock::WasmClock;
|
||||||
|
use super::code::WasmCode;
|
||||||
|
use super::comment::WasmComment;
|
||||||
|
use super::comment_block::WasmCommentBlock;
|
||||||
|
use super::diary_sexp::WasmDiarySexp;
|
||||||
|
use super::document::WasmDocument;
|
||||||
|
use super::drawer::WasmDrawer;
|
||||||
|
use super::dynamic_block::WasmDynamicBlock;
|
||||||
|
use super::entity::WasmEntity;
|
||||||
|
use super::example_block::WasmExampleBlock;
|
||||||
|
use super::export_block::WasmExportBlock;
|
||||||
|
use super::export_snippet::WasmExportSnippet;
|
||||||
|
use super::fixed_width_area::WasmFixedWidthArea;
|
||||||
|
use super::footnote_definition::WasmFootnoteDefinition;
|
||||||
|
use super::footnote_reference::WasmFootnoteReference;
|
||||||
|
use super::headline::WasmHeadline;
|
||||||
|
use super::horizontal_rule::WasmHorizontalRule;
|
||||||
|
use super::inline_babel_call::WasmInlineBabelCall;
|
||||||
|
use super::inline_source_block::WasmInlineSourceBlock;
|
||||||
|
use super::italic::WasmItalic;
|
||||||
|
use super::keyword::WasmKeyword;
|
||||||
|
use super::latex_environment::WasmLatexEnvironment;
|
||||||
|
use super::latex_fragment::WasmLatexFragment;
|
||||||
|
use super::line_break::WasmLineBreak;
|
||||||
|
use super::node_property::WasmNodeProperty;
|
||||||
|
use super::org_macro::WasmOrgMacro;
|
||||||
|
use super::paragraph::WasmParagraph;
|
||||||
|
use super::plain_link::WasmPlainLink;
|
||||||
|
use super::plain_list::WasmPlainList;
|
||||||
|
use super::plain_list_item::WasmPlainListItem;
|
||||||
|
use super::plain_text::WasmPlainText;
|
||||||
|
use super::planning::WasmPlanning;
|
||||||
|
use super::property_drawer::WasmPropertyDrawer;
|
||||||
|
use super::quote_block::WasmQuoteBlock;
|
||||||
|
use super::radio_link::WasmRadioLink;
|
||||||
|
use super::radio_target::WasmRadioTarget;
|
||||||
|
use super::regular_link::WasmRegularLink;
|
||||||
|
use super::section::WasmSection;
|
||||||
|
use super::special_block::WasmSpecialBlock;
|
||||||
|
use super::src_block::WasmSrcBlock;
|
||||||
|
use super::statistics_cookie::WasmStatisticsCookie;
|
||||||
|
use super::strike_through::WasmStrikeThrough;
|
||||||
|
use super::subscript::WasmSubscript;
|
||||||
|
use super::superscript::WasmSuperscript;
|
||||||
|
use super::table::WasmTable;
|
||||||
|
use super::table_cell::WasmTableCell;
|
||||||
|
use super::table_row::WasmTableRow;
|
||||||
|
use super::target::WasmTarget;
|
||||||
|
use super::timestamp::WasmTimestamp;
|
||||||
|
use super::underline::WasmUnderline;
|
||||||
|
use super::verbatim::WasmVerbatim;
|
||||||
|
use super::verse_block::WasmVerseBlock;
|
||||||
|
use super::WasmStandardProperties;
|
||||||
|
|
||||||
|
#[derive(Debug, Serialize, Deserialize)]
|
||||||
|
pub struct WasmAstNodeWrapper<I> {
|
||||||
|
#[serde(rename = "ast-node")]
|
||||||
|
pub(crate) ast_node: String,
|
||||||
|
#[serde(rename = "standard-properties")]
|
||||||
|
pub(crate) standard_properties: WasmStandardProperties,
|
||||||
|
#[serde(rename = "children")]
|
||||||
|
pub(crate) children: Vec<WasmAstNode>,
|
||||||
|
#[serde(rename = "properties")]
|
||||||
|
pub(crate) properties: I,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Serialize, Deserialize)]
|
||||||
|
#[serde(untagged)]
|
||||||
|
pub enum WasmAstNode {
|
||||||
|
// Document Nodes
|
||||||
|
Document(WasmAstNodeWrapper<WasmDocument>),
|
||||||
|
Headline(WasmAstNodeWrapper<WasmHeadline>),
|
||||||
|
Section(WasmAstNodeWrapper<WasmSection>),
|
||||||
|
// Elements
|
||||||
|
Paragraph(WasmAstNodeWrapper<WasmParagraph>),
|
||||||
|
PlainList(WasmAstNodeWrapper<WasmPlainList>),
|
||||||
|
PlainListItem(WasmAstNodeWrapper<WasmPlainListItem>),
|
||||||
|
CenterBlock(WasmAstNodeWrapper<WasmCenterBlock>),
|
||||||
|
QuoteBlock(WasmAstNodeWrapper<WasmQuoteBlock>),
|
||||||
|
SpecialBlock(WasmAstNodeWrapper<WasmSpecialBlock>),
|
||||||
|
DynamicBlock(WasmAstNodeWrapper<WasmDynamicBlock>),
|
||||||
|
FootnoteDefinition(WasmAstNodeWrapper<WasmFootnoteDefinition>),
|
||||||
|
Comment(WasmAstNodeWrapper<WasmComment>),
|
||||||
|
Drawer(WasmAstNodeWrapper<WasmDrawer>),
|
||||||
|
PropertyDrawer(WasmAstNodeWrapper<WasmPropertyDrawer>),
|
||||||
|
NodeProperty(WasmAstNodeWrapper<WasmNodeProperty>),
|
||||||
|
Table(WasmAstNodeWrapper<WasmTable>),
|
||||||
|
TableRow(WasmAstNodeWrapper<WasmTableRow>),
|
||||||
|
VerseBlock(WasmAstNodeWrapper<WasmVerseBlock>),
|
||||||
|
CommentBlock(WasmAstNodeWrapper<WasmCommentBlock>),
|
||||||
|
ExampleBlock(WasmAstNodeWrapper<WasmExampleBlock>),
|
||||||
|
ExportBlock(WasmAstNodeWrapper<WasmExportBlock>),
|
||||||
|
SrcBlock(WasmAstNodeWrapper<WasmSrcBlock>),
|
||||||
|
Clock(WasmAstNodeWrapper<WasmClock>),
|
||||||
|
DiarySexp(WasmAstNodeWrapper<WasmDiarySexp>),
|
||||||
|
Planning(WasmAstNodeWrapper<WasmPlanning>),
|
||||||
|
FixedWidthArea(WasmAstNodeWrapper<WasmFixedWidthArea>),
|
||||||
|
HorizontalRule(WasmAstNodeWrapper<WasmHorizontalRule>),
|
||||||
|
Keyword(WasmAstNodeWrapper<WasmKeyword>),
|
||||||
|
BabelCall(WasmAstNodeWrapper<WasmBabelCall>),
|
||||||
|
LatexEnvironment(WasmAstNodeWrapper<WasmLatexEnvironment>),
|
||||||
|
// Objects
|
||||||
|
Bold(WasmAstNodeWrapper<WasmBold>),
|
||||||
|
Italic(WasmAstNodeWrapper<WasmItalic>),
|
||||||
|
Underline(WasmAstNodeWrapper<WasmUnderline>),
|
||||||
|
StrikeThrough(WasmAstNodeWrapper<WasmStrikeThrough>),
|
||||||
|
Code(WasmAstNodeWrapper<WasmCode>),
|
||||||
|
Verbatim(WasmAstNodeWrapper<WasmVerbatim>),
|
||||||
|
PlainText(WasmAstNodeWrapper<WasmPlainText>),
|
||||||
|
RegularLink(WasmAstNodeWrapper<WasmRegularLink>),
|
||||||
|
RadioLink(WasmAstNodeWrapper<WasmRadioLink>),
|
||||||
|
RadioTarget(WasmAstNodeWrapper<WasmRadioTarget>),
|
||||||
|
PlainLink(WasmAstNodeWrapper<WasmPlainLink>),
|
||||||
|
AngleLink(WasmAstNodeWrapper<WasmAngleLink>),
|
||||||
|
OrgMacro(WasmAstNodeWrapper<WasmOrgMacro>),
|
||||||
|
Entity(WasmAstNodeWrapper<WasmEntity>),
|
||||||
|
LatexFragment(WasmAstNodeWrapper<WasmLatexFragment>),
|
||||||
|
ExportSnippet(WasmAstNodeWrapper<WasmExportSnippet>),
|
||||||
|
FootnoteReference(WasmAstNodeWrapper<WasmFootnoteReference>),
|
||||||
|
Citation(WasmAstNodeWrapper<WasmCitation>),
|
||||||
|
CitationReference(WasmAstNodeWrapper<WasmCitationReference>),
|
||||||
|
InlineBabelCall(WasmAstNodeWrapper<WasmInlineBabelCall>),
|
||||||
|
InlineSourceBlock(WasmAstNodeWrapper<WasmInlineSourceBlock>),
|
||||||
|
LineBreak(WasmAstNodeWrapper<WasmLineBreak>),
|
||||||
|
Target(WasmAstNodeWrapper<WasmTarget>),
|
||||||
|
StatisticsCookie(WasmAstNodeWrapper<WasmStatisticsCookie>),
|
||||||
|
Subscript(WasmAstNodeWrapper<WasmSubscript>),
|
||||||
|
Superscript(WasmAstNodeWrapper<WasmSuperscript>),
|
||||||
|
TableCell(WasmAstNodeWrapper<WasmTableCell>),
|
||||||
|
Timestamp(WasmAstNodeWrapper<WasmTimestamp>),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl WasmAstNode {}
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user