Compare commits
97 Commits
fb412bc2cd
...
v0.1.10
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4b54f95087 | ||
|
|
17c2e9fefe | ||
|
|
d2d8c1ffcf | ||
|
|
b9c638c280 | ||
|
|
8ac8f9fe6e | ||
|
|
ddb3144e66 | ||
|
|
ad5efc4b0f | ||
|
|
2de33b8150 | ||
|
|
e1fde88a60 | ||
|
|
123da9cca3 | ||
|
|
c20e7b5f2f | ||
|
|
74a3512038 | ||
|
|
ff04c4a131 | ||
|
|
00611e05c2 | ||
|
|
3e7e54a1bd | ||
|
|
aa35d1dc03 | ||
|
|
92afdc0ea6 | ||
|
|
f43920fc7c | ||
|
|
dde4bc7920 | ||
|
|
3d68e1fd00 | ||
|
|
8271f6b44a | ||
|
|
8a26965e14 | ||
|
|
3927889e66 | ||
|
|
5ecd7b8bef | ||
|
|
b0b795d13b | ||
|
|
182c2737cd | ||
|
|
5f93cabff5 | ||
|
|
a1f8cbe079 | ||
|
|
d7e870cba1 | ||
|
|
591b5ed382 | ||
|
|
fd141762f0 | ||
|
|
d59bbfa7d2 | ||
|
|
1d9f91cdd2 | ||
|
|
68f8e04ee8 | ||
|
|
c039e0d62c | ||
|
|
e6e3783ec6 | ||
|
|
5ae19e455d | ||
|
|
f475754a71 | ||
|
|
767f44f94d | ||
|
|
1411aca7b5 | ||
|
|
9ccdcaac24 | ||
|
|
d1223dcdb7 | ||
|
|
59448a4f2c | ||
|
|
5136880532 | ||
|
|
8654cf5507 | ||
|
|
6ca4dc8ffc | ||
|
|
a6f36ba679 | ||
|
|
176e37874e | ||
|
|
3208a04f7a | ||
|
|
0d579263cb | ||
|
|
c022b30110 | ||
|
|
7da4e4a29b | ||
|
|
8bc942a26f | ||
|
|
dff7550038 | ||
|
|
c4edcb8c24 | ||
|
|
4a44d88461 | ||
|
|
efc6bd11d9 | ||
|
|
51429e3155 | ||
|
|
b1a0fa4acf | ||
|
|
aeb2b6fe68 | ||
|
|
6679db98a8 | ||
|
|
5363324bbf | ||
|
|
dc9188dffc | ||
|
|
bd620ccd0d | ||
|
|
1947ae9f22 | ||
|
|
3fcf1b3864 | ||
|
|
d965dd6fd1 | ||
|
|
42dcd41e48 | ||
|
|
68fac7cfe8 | ||
|
|
a9f067e25a | ||
|
|
41b4b1015e | ||
|
|
9523365090 | ||
|
|
aa33fe42a8 | ||
|
|
78a9b93f22 | ||
|
|
a89339e472 | ||
|
|
f3307a8159 | ||
|
|
4bfa25dcb3 | ||
|
|
441a240c33 | ||
|
|
e767892dd5 | ||
|
|
8e0a7dea0f | ||
|
|
c4cc40f5e1 | ||
|
|
a5129199c7 | ||
|
|
c0e462944d | ||
|
|
63614841e8 | ||
|
|
bc4c09c546 | ||
|
|
0d1ab0e75a | ||
|
|
6e39029f91 | ||
|
|
d550966516 | ||
|
|
32d6b3d1ee | ||
|
|
7cb71a5a0a | ||
|
|
6b90a9bfa8 | ||
|
|
83d939bdc0 | ||
|
|
0e1ace78f4 | ||
|
|
6d4db60688 | ||
|
|
3aca01891d | ||
|
|
275ed87c3f | ||
|
|
b385270d7b |
@@ -137,7 +137,7 @@ spec:
|
||||
value: []
|
||||
- name: docker-image
|
||||
value: "$(params.image-name):$(tasks.fetch-repository.results.commit)"
|
||||
- name: run-image-all
|
||||
- name: run-image-tracing-compare
|
||||
taskRef:
|
||||
name: run-docker-image
|
||||
workspaces:
|
||||
@@ -152,6 +152,46 @@ spec:
|
||||
value: ["--no-default-features", "--features", "tracing,compare"]
|
||||
- name: docker-image
|
||||
value: "$(params.image-name):$(tasks.fetch-repository.results.commit)"
|
||||
- name: run-image-compare-foreign
|
||||
taskRef:
|
||||
name: run-docker-image
|
||||
workspaces:
|
||||
- name: source
|
||||
workspace: git-source
|
||||
- name: cargo-cache
|
||||
workspace: cargo-cache
|
||||
runAfter:
|
||||
- run-image-tracing-compare
|
||||
params:
|
||||
- name: args
|
||||
value:
|
||||
[
|
||||
"--no-default-features",
|
||||
"--features",
|
||||
"compare,foreign_document_test",
|
||||
]
|
||||
- name: docker-image
|
||||
value: "$(params.image-name):$(tasks.fetch-repository.results.commit)"
|
||||
- name: run-image-all
|
||||
taskRef:
|
||||
name: run-docker-image
|
||||
workspaces:
|
||||
- name: source
|
||||
workspace: git-source
|
||||
- name: cargo-cache
|
||||
workspace: cargo-cache
|
||||
runAfter:
|
||||
- run-image-compare-foreign
|
||||
params:
|
||||
- name: args
|
||||
value:
|
||||
[
|
||||
"--no-default-features",
|
||||
"--features",
|
||||
"tracing,compare,foreign_document_test",
|
||||
]
|
||||
- name: docker-image
|
||||
value: "$(params.image-name):$(tasks.fetch-repository.results.commit)"
|
||||
finally:
|
||||
- name: report-success
|
||||
when:
|
||||
|
||||
13
Cargo.toml
13
Cargo.toml
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "organic"
|
||||
version = "0.1.9"
|
||||
version = "0.1.10"
|
||||
authors = ["Tom Alexander <tom@fizz.buzz>"]
|
||||
description = "An org-mode parser."
|
||||
edition = "2021"
|
||||
@@ -31,7 +31,14 @@ path = "src/lib.rs"
|
||||
path = "src/bin_compare.rs"
|
||||
required-features = ["compare"]
|
||||
|
||||
[[bin]]
|
||||
# This bin exists for development purposes only. The real target of this crate is the library.
|
||||
name = "foreign_document_test"
|
||||
path = "src/bin_foreign_document_test.rs"
|
||||
required-features = ["foreign_document_test"]
|
||||
|
||||
[dependencies]
|
||||
futures = { version = "0.3.28", optional = true }
|
||||
nom = "7.1.1"
|
||||
opentelemetry = { version = "0.20.0", optional = true, default-features = false, features = ["trace", "rt-tokio"] }
|
||||
opentelemetry-otlp = { version = "0.13.0", optional = true }
|
||||
@@ -40,13 +47,15 @@ tokio = { version = "1.30.0", optional = true, default-features = false, feature
|
||||
tracing = { version = "0.1.37", optional = true }
|
||||
tracing-opentelemetry = { version = "0.20.0", optional = true }
|
||||
tracing-subscriber = { version = "0.3.17", optional = true, features = ["env-filter"] }
|
||||
walkdir = { version = "2.3.3", optional = true }
|
||||
|
||||
[build-dependencies]
|
||||
walkdir = "2.3.3"
|
||||
|
||||
[features]
|
||||
default = []
|
||||
compare = []
|
||||
compare = ["tokio/process", "tokio/macros"]
|
||||
foreign_document_test = ["compare", "dep:futures", "tokio/sync", "dep:walkdir", "tokio/process"]
|
||||
tracing = ["dep:opentelemetry", "dep:opentelemetry-otlp", "dep:opentelemetry-semantic-conventions", "dep:tokio", "dep:tracing", "dep:tracing-opentelemetry", "dep:tracing-subscriber"]
|
||||
|
||||
# Optimized build for any sort of release.
|
||||
|
||||
2
Makefile
2
Makefile
@@ -52,6 +52,8 @@ buildtest:
|
||||
> cargo build --no-default-features --features compare
|
||||
> cargo build --no-default-features --features 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,tracing,foreign_document_test
|
||||
|
||||
.PHONY: foreign_document_test
|
||||
foreign_document_test:
|
||||
|
||||
@@ -6,14 +6,13 @@ Organic is an emacs-less implementation of an [org-mode](https://orgmode.org/) p
|
||||
|
||||
This project is still under HEAVY development. While the version remains v0.1.x the API will be changing often. Once we hit v0.2.x we will start following semver.
|
||||
|
||||
Currently, the parser is able to correctly identify the start/end bounds of all the org-mode objects and elements (except table.el tables, org-mode tables are supported) but many of the interior properties are not yet populated.
|
||||
Currently, Organic parses most documents the same as the official org-mode parser. Most of the development right now is finding documents where the parsers differ and fixing those issues.
|
||||
|
||||
### Project Goals
|
||||
- We aim to provide perfect parity with the emacs org-mode parser. In that regard, any document that parses differently between Emacs and Organic is considered a bug.
|
||||
- The parser should be fast. We're not doing anything special, but since this is written in Rust and natively compiled we should be able to beat the existing parsers.
|
||||
- The parser should have minimal dependencies. This should reduce effort w.r.t.: security audits, legal compliance, portability.
|
||||
- The parser should be usable everywhere. In the interest of getting org-mode used in as many places as possible, this parser should be usable by everyone everywhere. This means:
|
||||
- It must have a permissive license for use in proprietary code bases.
|
||||
- It must have a permissive license.
|
||||
- We will investigate compiling to WASM. This is an important goal of the project and will definitely happen, but only after the parser has a more stable API.
|
||||
- We will investigate compiling to a C library for native linking to other code. This is more of a maybe-goal for the project.
|
||||
### Project Non-Goals
|
||||
|
||||
@@ -14,7 +14,7 @@ RUN make DESTDIR="/root/dist" install
|
||||
|
||||
|
||||
FROM build AS build-org-mode
|
||||
ARG ORG_VERSION=c703541ffcc14965e3567f928de1683a1c1e33f6
|
||||
ARG ORG_VERSION=abf5156096c06ee5aa05795c3dc5a065f76ada97
|
||||
COPY --from=build-emacs /root/dist/ /
|
||||
RUN mkdir /root/dist
|
||||
# Savannah does not allow fetching specific revisions, so we're going to have to put unnecessary load on their server by cloning main and then checking out the revision we want.
|
||||
@@ -102,6 +102,4 @@ COPY --from=foreign-document-gather /foreign_documents/doomemacs /foreign_docume
|
||||
COPY --from=foreign-document-gather /foreign_documents/worg /foreign_documents/worg
|
||||
COPY --from=build-org-mode /root/org-mode /foreign_documents/org-mode
|
||||
COPY --from=build-emacs /root/emacs /foreign_documents/emacs
|
||||
COPY foreign_document_test_entrypoint.sh /entrypoint.sh
|
||||
RUN chmod +x /entrypoint.sh
|
||||
ENTRYPOINT ["/entrypoint.sh"]
|
||||
ENTRYPOINT ["cargo", "run", "--bin", "foreign_document_test", "--features", "compare,foreign_document_test", "--profile", "release-lto"]
|
||||
|
||||
@@ -1,149 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
#
|
||||
# Run the Organic compare script against a series of documents sourced from exterior places.
|
||||
set -euo pipefail
|
||||
IFS=$'\n\t'
|
||||
DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
|
||||
|
||||
REALPATH=$(command -v uu-realpath || command -v realpath)
|
||||
|
||||
function log {
|
||||
(>&2 echo "${@}")
|
||||
}
|
||||
|
||||
function die {
|
||||
local status_code="$1"
|
||||
shift
|
||||
(>&2 echo "${@}")
|
||||
exit "$status_code"
|
||||
}
|
||||
|
||||
function main {
|
||||
cargo build --no-default-features --features compare --profile release-lto
|
||||
if [ "${CARGO_TARGET_DIR:-}" = "" ]; then
|
||||
CARGO_TARGET_DIR=$(realpath target/)
|
||||
fi
|
||||
PARSE="${CARGO_TARGET_DIR}/release-lto/compare"
|
||||
|
||||
local all_status=0
|
||||
set +e
|
||||
|
||||
(run_compare_function "org-mode" compare_all_org_document "/foreign_documents/org-mode")
|
||||
if [ "$?" -ne 0 ]; then all_status=1; fi
|
||||
(run_compare_function "emacs" compare_all_org_document "/foreign_documents/emacs")
|
||||
if [ "$?" -ne 0 ]; then all_status=1; fi
|
||||
(run_compare_function "worg" compare_all_org_document "/foreign_documents/worg")
|
||||
if [ "$?" -ne 0 ]; then all_status=1; fi
|
||||
(run_compare_function "howard_abrams" compare_howard_abrams)
|
||||
if [ "$?" -ne 0 ]; then all_status=1; fi
|
||||
(run_compare_function "doomemacs" compare_all_org_document "/foreign_documents/doomemacs")
|
||||
if [ "$?" -ne 0 ]; then all_status=1; fi
|
||||
|
||||
set -e
|
||||
if [ "$all_status" -ne 0 ]; then
|
||||
red_text "Some tests failed."
|
||||
else
|
||||
green_text "All tests passed."
|
||||
fi
|
||||
return "$all_status"
|
||||
}
|
||||
|
||||
function green_text {
|
||||
(IFS=' '; printf '\x1b[38;2;0;255;0m%s\x1b[0m' "${*}")
|
||||
}
|
||||
|
||||
function red_text {
|
||||
(IFS=' '; printf '\x1b[38;2;255;0;0m%s\x1b[0m' "${*}")
|
||||
}
|
||||
|
||||
function yellow_text {
|
||||
(IFS=' '; printf '\x1b[38;2;255;255;0m%s\x1b[0m' "${*}")
|
||||
}
|
||||
|
||||
function indent {
|
||||
local depth="$1"
|
||||
local scaled_depth=$((depth * 2))
|
||||
shift 1
|
||||
local prefix
|
||||
prefix=$(printf -- "%${scaled_depth}s")
|
||||
while read -r l; do
|
||||
(IFS=' '; printf -- '%s%s\n' "$prefix" "$l")
|
||||
done
|
||||
}
|
||||
|
||||
function run_compare_function {
|
||||
local name="$1"
|
||||
local stdoutput
|
||||
shift 1
|
||||
set +e
|
||||
stdoutput=$("${@}")
|
||||
local status=$?
|
||||
set -e
|
||||
if [ "$status" -eq 0 ]; then
|
||||
echo "$(green_text "GOOD") $name"
|
||||
indent 1 <<<"$stdoutput"
|
||||
else
|
||||
echo "$(red_text "FAIL") $name"
|
||||
indent 1 <<<"$stdoutput"
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
function compare_all_org_document {
|
||||
local root_dir="$1"
|
||||
local target_document
|
||||
local all_status=0
|
||||
while read target_document; do
|
||||
local relative_path
|
||||
relative_path=$($REALPATH --relative-to "$root_dir" "$target_document")
|
||||
set +e
|
||||
(run_compare "$relative_path" "$target_document")
|
||||
if [ "$?" -ne 0 ]; then all_status=1; fi
|
||||
set -e
|
||||
done<<<"$(find "$root_dir" -type f -iname '*.org' | sort)"
|
||||
return "$all_status"
|
||||
}
|
||||
|
||||
function run_compare {
|
||||
local name="$1"
|
||||
local target_document="$2"
|
||||
set +e
|
||||
($PARSE "$target_document" &> /dev/null)
|
||||
local status=$?
|
||||
set -e
|
||||
if [ "$status" -eq 0 ]; then
|
||||
echo "$(green_text "GOOD") $name"
|
||||
else
|
||||
echo "$(red_text "FAIL") $name"
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
function compare_howard_abrams {
|
||||
local all_status=0
|
||||
set +e
|
||||
|
||||
(run_compare_function "dot-files" compare_all_org_document "/foreign_documents/howardabrams/dot-files")
|
||||
if [ "$?" -ne 0 ]; then all_status=1; fi
|
||||
(run_compare_function "hamacs" compare_all_org_document "/foreign_documents/howardabrams/hamacs")
|
||||
if [ "$?" -ne 0 ]; then all_status=1; fi
|
||||
(run_compare_function "demo-it" compare_all_org_document "/foreign_documents/howardabrams/demo-it")
|
||||
if [ "$?" -ne 0 ]; then all_status=1; fi
|
||||
(run_compare_function "magit-demo" compare_all_org_document "/foreign_documents/howardabrams/magit-demo")
|
||||
if [ "$?" -ne 0 ]; then all_status=1; fi
|
||||
(run_compare_function "pdx-emacs-hackers" compare_all_org_document "/foreign_documents/howardabrams/pdx-emacs-hackers")
|
||||
if [ "$?" -ne 0 ]; then all_status=1; fi
|
||||
(run_compare_function "flora-simulator" compare_all_org_document "/foreign_documents/howardabrams/flora-simulator")
|
||||
if [ "$?" -ne 0 ]; then all_status=1; fi
|
||||
(run_compare_function "literate-devops-demo" compare_all_org_document "/foreign_documents/howardabrams/literate-devops-demo")
|
||||
if [ "$?" -ne 0 ]; then all_status=1; fi
|
||||
(run_compare_function "clojure-yesql-xp" compare_all_org_document "/foreign_documents/howardabrams/clojure-yesql-xp")
|
||||
if [ "$?" -ne 0 ]; then all_status=1; fi
|
||||
(run_compare_function "veep" compare_all_org_document "/foreign_documents/howardabrams/veep")
|
||||
if [ "$?" -ne 0 ]; then all_status=1; fi
|
||||
|
||||
set -e
|
||||
return "$all_status"
|
||||
}
|
||||
|
||||
main "${@}"
|
||||
8
org_mode_samples/affiliated_keyword/optional_value.org
Normal file
8
org_mode_samples/affiliated_keyword/optional_value.org
Normal file
@@ -0,0 +1,8 @@
|
||||
#+CAPTION[foo]: *bar*
|
||||
#+CAPTION[*lorem* ipsum]: dolar
|
||||
1. baz
|
||||
|
||||
|
||||
#+CAPTION[foo]: *bar*
|
||||
#+CAPTION[*lorem* ipsum]: dolar
|
||||
# Comments cannot have affiliated keywords so those become regular keywords.
|
||||
@@ -0,0 +1,74 @@
|
||||
* Headline
|
||||
before
|
||||
#+NAME: a
|
||||
#+caption: b *lorem* ipsum
|
||||
#+results: c
|
||||
#+headers: d
|
||||
#+header: e
|
||||
#+label: f
|
||||
#+plot: g
|
||||
#+resname: h
|
||||
#+result: i
|
||||
#+source: j
|
||||
#+srcname: k
|
||||
#+tblname: l
|
||||
#+attr_latex: m
|
||||
#+attr_html: n
|
||||
#+NAME: aa
|
||||
#+caption: bb *lorem* ipsum
|
||||
#+results: cc
|
||||
#+headers: dd
|
||||
#+header: ee
|
||||
#+label: ff
|
||||
#+plot: gg
|
||||
#+resname: hh
|
||||
#+result: ii
|
||||
#+source: jj
|
||||
#+srcname: kk
|
||||
#+tblname: ll
|
||||
#+attr_latex: mm
|
||||
#+attr_html: nn
|
||||
:candle:
|
||||
inside
|
||||
|
||||
the drawer
|
||||
:end:
|
||||
|
||||
|
||||
|
||||
|
||||
#+NAME: A
|
||||
#+CAPTION: B *LOREM* IPSUM
|
||||
#+RESULTS: C
|
||||
#+HEADERS: D
|
||||
#+HEADER: E
|
||||
#+LABEL: F
|
||||
#+PLOT: G
|
||||
#+RESNAME: H
|
||||
#+RESULT: I
|
||||
#+SOURCE: J
|
||||
#+SRCNAME: K
|
||||
#+TBLNAME: L
|
||||
#+ATTR_LATEX: M
|
||||
#+ATTR_HTML: N
|
||||
#+NAME: AA
|
||||
#+CAPTION: BB *LOREM* IPSUM
|
||||
#+RESULTS: CC
|
||||
#+HEADERS: DD
|
||||
#+HEADER: EE
|
||||
#+LABEL: FF
|
||||
#+PLOT: GG
|
||||
#+RESNAME: HH
|
||||
#+RESULT: II
|
||||
#+SOURCE: JJ
|
||||
#+SRCNAME: KK
|
||||
#+TBLNAME: LL
|
||||
#+ATTR_LATEX: MM
|
||||
#+ATTR_HTML: NN
|
||||
:candle:
|
||||
inside
|
||||
|
||||
the drawer
|
||||
:end:
|
||||
|
||||
after
|
||||
@@ -1,10 +0,0 @@
|
||||
* Headline
|
||||
before
|
||||
#+NAME: foo
|
||||
:candle:
|
||||
inside
|
||||
|
||||
the drawer
|
||||
:end:
|
||||
|
||||
after
|
||||
@@ -0,0 +1,72 @@
|
||||
#+NAME: a
|
||||
#+caption: b *lorem* ipsum
|
||||
#+results: c
|
||||
#+headers: d
|
||||
#+header: e
|
||||
#+label: f
|
||||
#+plot: g
|
||||
#+resname: h
|
||||
#+result: i
|
||||
#+source: j
|
||||
#+srcname: k
|
||||
#+tblname: l
|
||||
#+attr_latex: m
|
||||
#+attr_html: n
|
||||
#+NAME: aa
|
||||
#+caption: bb *lorem* ipsum
|
||||
#+results: cc
|
||||
#+headers: dd
|
||||
#+header: ee
|
||||
#+label: ff
|
||||
#+plot: gg
|
||||
#+resname: hh
|
||||
#+result: ii
|
||||
#+source: jj
|
||||
#+srcname: kk
|
||||
#+tblname: ll
|
||||
#+attr_latex: mm
|
||||
#+attr_html: nn
|
||||
#+BEGIN: clocktable :scope file :maxlevel 2
|
||||
#+CAPTION: Clock summary at [2023-08-25 Fri 05:34]
|
||||
| Headline | Time |
|
||||
|--------------+--------|
|
||||
| *Total time* | *0:00* |
|
||||
#+END:
|
||||
|
||||
|
||||
|
||||
|
||||
#+NAME: A
|
||||
#+CAPTION: B *LOREM* IPSUM
|
||||
#+RESULTS: C
|
||||
#+HEADERS: D
|
||||
#+HEADER: E
|
||||
#+LABEL: F
|
||||
#+PLOT: G
|
||||
#+RESNAME: H
|
||||
#+RESULT: I
|
||||
#+SOURCE: J
|
||||
#+SRCNAME: K
|
||||
#+TBLNAME: L
|
||||
#+ATTR_LATEX: M
|
||||
#+ATTR_HTML: N
|
||||
#+NAME: AA
|
||||
#+CAPTION: BB *LOREM* IPSUM
|
||||
#+RESULTS: CC
|
||||
#+HEADERS: DD
|
||||
#+HEADER: EE
|
||||
#+LABEL: FF
|
||||
#+PLOT: GG
|
||||
#+RESNAME: HH
|
||||
#+RESULT: II
|
||||
#+SOURCE: JJ
|
||||
#+SRCNAME: KK
|
||||
#+TBLNAME: LL
|
||||
#+ATTR_LATEX: MM
|
||||
#+ATTR_HTML: NN
|
||||
#+BEGIN: clocktable :scope file :maxlevel 2
|
||||
#+CAPTION: Clock summary at [2023-08-25 Fri 05:34]
|
||||
| Headline | Time |
|
||||
|--------------+--------|
|
||||
| *Total time* | *0:00* |
|
||||
#+END:
|
||||
@@ -1,7 +0,0 @@
|
||||
#+NAME: foo
|
||||
#+BEGIN: clocktable :scope file :maxlevel 2
|
||||
#+CAPTION: Clock summary at [2023-08-25 Fri 05:34]
|
||||
| Headline | Time |
|
||||
|--------------+--------|
|
||||
| *Total time* | *0:00* |
|
||||
#+END:
|
||||
@@ -0,0 +1,63 @@
|
||||
#+NAME: a
|
||||
#+caption: b *lorem* ipsum
|
||||
#+results: c
|
||||
#+headers: d
|
||||
#+header: e
|
||||
#+label: f
|
||||
#+plot: g
|
||||
#+resname: h
|
||||
#+result: i
|
||||
#+source: j
|
||||
#+srcname: k
|
||||
#+tblname: l
|
||||
#+attr_latex: m
|
||||
#+attr_html: n
|
||||
#+NAME: aa
|
||||
#+caption: bb *lorem* ipsum
|
||||
#+results: cc
|
||||
#+headers: dd
|
||||
#+header: ee
|
||||
#+label: ff
|
||||
#+plot: gg
|
||||
#+resname: hh
|
||||
#+result: ii
|
||||
#+source: jj
|
||||
#+srcname: kk
|
||||
#+tblname: ll
|
||||
#+attr_latex: mm
|
||||
#+attr_html: nn
|
||||
[fn:1] A footnote.
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
#+NAME: A
|
||||
#+CAPTION: B *LOREM* IPSUM
|
||||
#+RESULTS: C
|
||||
#+HEADERS: D
|
||||
#+HEADER: E
|
||||
#+LABEL: F
|
||||
#+PLOT: G
|
||||
#+RESNAME: H
|
||||
#+RESULT: I
|
||||
#+SOURCE: J
|
||||
#+SRCNAME: K
|
||||
#+TBLNAME: L
|
||||
#+ATTR_LATEX: M
|
||||
#+ATTR_HTML: N
|
||||
#+NAME: AA
|
||||
#+CAPTION: BB *LOREM* IPSUM
|
||||
#+RESULTS: CC
|
||||
#+HEADERS: DD
|
||||
#+HEADER: EE
|
||||
#+LABEL: FF
|
||||
#+PLOT: GG
|
||||
#+RESNAME: HH
|
||||
#+RESULT: II
|
||||
#+SOURCE: JJ
|
||||
#+SRCNAME: KK
|
||||
#+TBLNAME: LL
|
||||
#+ATTR_LATEX: MM
|
||||
#+ATTR_HTML: NN
|
||||
[fn:1] A footnote.
|
||||
@@ -0,0 +1,67 @@
|
||||
#+NAME: a
|
||||
#+caption: b *lorem* ipsum
|
||||
#+results: c
|
||||
#+headers: d
|
||||
#+header: e
|
||||
#+label: f
|
||||
#+plot: g
|
||||
#+resname: h
|
||||
#+result: i
|
||||
#+source: j
|
||||
#+srcname: k
|
||||
#+tblname: l
|
||||
#+attr_latex: m
|
||||
#+attr_html: n
|
||||
#+NAME: aa
|
||||
#+caption: bb *lorem* ipsum
|
||||
#+results: cc
|
||||
#+headers: dd
|
||||
#+header: ee
|
||||
#+label: ff
|
||||
#+plot: gg
|
||||
#+resname: hh
|
||||
#+result: ii
|
||||
#+source: jj
|
||||
#+srcname: kk
|
||||
#+tblname: ll
|
||||
#+attr_latex: mm
|
||||
#+attr_html: nn
|
||||
#+begin_center
|
||||
|
||||
#+end_center
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
#+NAME: A
|
||||
#+CAPTION: B *LOREM* IPSUM
|
||||
#+RESULTS: C
|
||||
#+HEADERS: D
|
||||
#+HEADER: E
|
||||
#+LABEL: F
|
||||
#+PLOT: G
|
||||
#+RESNAME: H
|
||||
#+RESULT: I
|
||||
#+SOURCE: J
|
||||
#+SRCNAME: K
|
||||
#+TBLNAME: L
|
||||
#+ATTR_LATEX: M
|
||||
#+ATTR_HTML: N
|
||||
#+NAME: AA
|
||||
#+CAPTION: BB *LOREM* IPSUM
|
||||
#+RESULTS: CC
|
||||
#+HEADERS: DD
|
||||
#+HEADER: EE
|
||||
#+LABEL: FF
|
||||
#+PLOT: GG
|
||||
#+RESNAME: HH
|
||||
#+RESULT: II
|
||||
#+SOURCE: JJ
|
||||
#+SRCNAME: KK
|
||||
#+TBLNAME: LL
|
||||
#+ATTR_LATEX: MM
|
||||
#+ATTR_HTML: NN
|
||||
#+begin_center
|
||||
|
||||
#+end_center
|
||||
@@ -1,4 +0,0 @@
|
||||
#+NAME: foo
|
||||
#+begin_center
|
||||
|
||||
#+end_center
|
||||
@@ -0,0 +1,67 @@
|
||||
#+NAME: a
|
||||
#+caption: b *lorem* ipsum
|
||||
#+results: c
|
||||
#+headers: d
|
||||
#+header: e
|
||||
#+label: f
|
||||
#+plot: g
|
||||
#+resname: h
|
||||
#+result: i
|
||||
#+source: j
|
||||
#+srcname: k
|
||||
#+tblname: l
|
||||
#+attr_latex: m
|
||||
#+attr_html: n
|
||||
#+NAME: aa
|
||||
#+caption: bb *lorem* ipsum
|
||||
#+results: cc
|
||||
#+headers: dd
|
||||
#+header: ee
|
||||
#+label: ff
|
||||
#+plot: gg
|
||||
#+resname: hh
|
||||
#+result: ii
|
||||
#+source: jj
|
||||
#+srcname: kk
|
||||
#+tblname: ll
|
||||
#+attr_latex: mm
|
||||
#+attr_html: nn
|
||||
#+begin_quote
|
||||
|
||||
#+end_quote
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
#+NAME: A
|
||||
#+CAPTION: B *LOREM* IPSUM
|
||||
#+RESULTS: C
|
||||
#+HEADERS: D
|
||||
#+HEADER: E
|
||||
#+LABEL: F
|
||||
#+PLOT: G
|
||||
#+RESNAME: H
|
||||
#+RESULT: I
|
||||
#+SOURCE: J
|
||||
#+SRCNAME: K
|
||||
#+TBLNAME: L
|
||||
#+ATTR_LATEX: M
|
||||
#+ATTR_HTML: N
|
||||
#+NAME: AA
|
||||
#+CAPTION: BB *LOREM* IPSUM
|
||||
#+RESULTS: CC
|
||||
#+HEADERS: DD
|
||||
#+HEADER: EE
|
||||
#+LABEL: FF
|
||||
#+PLOT: GG
|
||||
#+RESNAME: HH
|
||||
#+RESULT: II
|
||||
#+SOURCE: JJ
|
||||
#+SRCNAME: KK
|
||||
#+TBLNAME: LL
|
||||
#+ATTR_LATEX: MM
|
||||
#+ATTR_HTML: NN
|
||||
#+begin_quote
|
||||
|
||||
#+end_quote
|
||||
@@ -1,4 +0,0 @@
|
||||
#+NAME: foo
|
||||
#+begin_quote
|
||||
|
||||
#+end_quote
|
||||
@@ -0,0 +1,71 @@
|
||||
#+NAME: a
|
||||
#+caption: b *lorem* ipsum
|
||||
#+results: c
|
||||
#+headers: d
|
||||
#+header: e
|
||||
#+label: f
|
||||
#+plot: g
|
||||
#+resname: h
|
||||
#+result: i
|
||||
#+source: j
|
||||
#+srcname: k
|
||||
#+tblname: l
|
||||
#+attr_latex: m
|
||||
#+attr_html: n
|
||||
#+NAME: aa
|
||||
#+caption: bb *lorem* ipsum
|
||||
#+results: cc
|
||||
#+headers: dd
|
||||
#+header: ee
|
||||
#+label: ff
|
||||
#+plot: gg
|
||||
#+resname: hh
|
||||
#+result: ii
|
||||
#+source: jj
|
||||
#+srcname: kk
|
||||
#+tblname: ll
|
||||
#+attr_latex: mm
|
||||
#+attr_html: nn
|
||||
#+begin_defun
|
||||
foo
|
||||
|
||||
{{{bar(baz)}}}
|
||||
#+end_defun
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
#+NAME: A
|
||||
#+CAPTION: B *LOREM* IPSUM
|
||||
#+RESULTS: C
|
||||
#+HEADERS: D
|
||||
#+HEADER: E
|
||||
#+LABEL: F
|
||||
#+PLOT: G
|
||||
#+RESNAME: H
|
||||
#+RESULT: I
|
||||
#+SOURCE: J
|
||||
#+SRCNAME: K
|
||||
#+TBLNAME: L
|
||||
#+ATTR_LATEX: M
|
||||
#+ATTR_HTML: N
|
||||
#+NAME: AA
|
||||
#+CAPTION: BB *LOREM* IPSUM
|
||||
#+RESULTS: CC
|
||||
#+HEADERS: DD
|
||||
#+HEADER: EE
|
||||
#+LABEL: FF
|
||||
#+PLOT: GG
|
||||
#+RESNAME: HH
|
||||
#+RESULT: II
|
||||
#+SOURCE: JJ
|
||||
#+SRCNAME: KK
|
||||
#+TBLNAME: LL
|
||||
#+ATTR_LATEX: MM
|
||||
#+ATTR_HTML: NN
|
||||
#+begin_defun
|
||||
foo
|
||||
|
||||
{{{bar(baz)}}}
|
||||
#+end_defun
|
||||
@@ -1,6 +0,0 @@
|
||||
#+NAME: foo
|
||||
#+begin_defun
|
||||
foo
|
||||
|
||||
{{{bar(baz)}}}
|
||||
#+end_defun
|
||||
@@ -0,0 +1,63 @@
|
||||
#+NAME: a
|
||||
#+caption: b *lorem* ipsum
|
||||
#+results: c
|
||||
#+headers: d
|
||||
#+header: e
|
||||
#+label: f
|
||||
#+plot: g
|
||||
#+resname: h
|
||||
#+result: i
|
||||
#+source: j
|
||||
#+srcname: k
|
||||
#+tblname: l
|
||||
#+attr_latex: m
|
||||
#+attr_html: n
|
||||
#+NAME: aa
|
||||
#+caption: bb *lorem* ipsum
|
||||
#+results: cc
|
||||
#+headers: dd
|
||||
#+header: ee
|
||||
#+label: ff
|
||||
#+plot: gg
|
||||
#+resname: hh
|
||||
#+result: ii
|
||||
#+source: jj
|
||||
#+srcname: kk
|
||||
#+tblname: ll
|
||||
#+attr_latex: mm
|
||||
#+attr_html: nn
|
||||
1. bar
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
#+NAME: A
|
||||
#+CAPTION: B *LOREM* IPSUM
|
||||
#+RESULTS: C
|
||||
#+HEADERS: D
|
||||
#+HEADER: E
|
||||
#+LABEL: F
|
||||
#+PLOT: G
|
||||
#+RESNAME: H
|
||||
#+RESULT: I
|
||||
#+SOURCE: J
|
||||
#+SRCNAME: K
|
||||
#+TBLNAME: L
|
||||
#+ATTR_LATEX: M
|
||||
#+ATTR_HTML: N
|
||||
#+NAME: AA
|
||||
#+CAPTION: BB *LOREM* IPSUM
|
||||
#+RESULTS: CC
|
||||
#+HEADERS: DD
|
||||
#+HEADER: EE
|
||||
#+LABEL: FF
|
||||
#+PLOT: GG
|
||||
#+RESNAME: HH
|
||||
#+RESULT: II
|
||||
#+SOURCE: JJ
|
||||
#+SRCNAME: KK
|
||||
#+TBLNAME: LL
|
||||
#+ATTR_LATEX: MM
|
||||
#+ATTR_HTML: NN
|
||||
1. BAR
|
||||
@@ -0,0 +1,3 @@
|
||||
1. foo
|
||||
#+NAME: bar
|
||||
2. baz
|
||||
@@ -1,2 +0,0 @@
|
||||
#+NAME: foo
|
||||
1. bar
|
||||
@@ -0,0 +1,5 @@
|
||||
* baz
|
||||
# lorem
|
||||
:PROPERTIES:
|
||||
:FOO: bar
|
||||
:END:
|
||||
@@ -0,0 +1,6 @@
|
||||
* baz
|
||||
# lorem
|
||||
|
||||
:PROPERTIES:
|
||||
:FOO: bar
|
||||
:END:
|
||||
@@ -0,0 +1,4 @@
|
||||
* baz
|
||||
:PROPERTIES:
|
||||
:FOO: bar
|
||||
:END:
|
||||
@@ -0,0 +1,5 @@
|
||||
* baz
|
||||
|
||||
:PROPERTIES:
|
||||
:FOO: bar
|
||||
:END:
|
||||
@@ -0,0 +1,13 @@
|
||||
* baz
|
||||
|
||||
|
||||
|
||||
|
||||
# lorem
|
||||
|
||||
|
||||
|
||||
|
||||
:PROPERTIES:
|
||||
:FOO: bar
|
||||
:END:
|
||||
@@ -0,0 +1,19 @@
|
||||
:PROPERTIES:
|
||||
:foo: bar
|
||||
:foo+: baz
|
||||
:cat: dog
|
||||
:END:
|
||||
|
||||
# Even though these are inheriting the properties and overwriting and/or appending to them, this is not represented in the parser AST so Organic does not do any special handling of this.
|
||||
|
||||
* Overwrite
|
||||
:PROPERTIES:
|
||||
:foo: lorem
|
||||
:bat: car
|
||||
:END:
|
||||
|
||||
* Append
|
||||
:PROPERTIES:
|
||||
:foo+: ipsum
|
||||
:cake: lie
|
||||
:END:
|
||||
@@ -0,0 +1,5 @@
|
||||
* Overwrite
|
||||
#+NAME: foo
|
||||
:PROPERTIES:
|
||||
:header-args: :var foo="lorem"
|
||||
:END:
|
||||
@@ -1,25 +0,0 @@
|
||||
|
||||
|
||||
|
||||
|
||||
# Blank lines and comments can come before property drawers in the zeroth section
|
||||
:PROPERTIES:
|
||||
:FOO: bar
|
||||
:END:
|
||||
|
||||
|
||||
|
||||
* Spaces turn property drawers into regular drawers
|
||||
|
||||
:PROPERTIES:
|
||||
:FOO: bar
|
||||
:END:
|
||||
* Comments turn property drawers into regular drawers
|
||||
# Comment
|
||||
:PROPERTIES:
|
||||
:FOO: bar
|
||||
:END:
|
||||
* Baseline
|
||||
:PROPERTIES:
|
||||
:FOO: bar
|
||||
:END:
|
||||
@@ -0,0 +1,4 @@
|
||||
# lorem
|
||||
:PROPERTIES:
|
||||
:FOO: bar
|
||||
:END:
|
||||
@@ -0,0 +1,5 @@
|
||||
# lorem
|
||||
|
||||
:PROPERTIES:
|
||||
:FOO: bar
|
||||
:END:
|
||||
@@ -0,0 +1,3 @@
|
||||
:PROPERTIES:
|
||||
:FOO: bar
|
||||
:END:
|
||||
@@ -0,0 +1,4 @@
|
||||
|
||||
:PROPERTIES:
|
||||
:FOO: bar
|
||||
:END:
|
||||
@@ -0,0 +1,12 @@
|
||||
|
||||
|
||||
|
||||
|
||||
# lorem
|
||||
|
||||
|
||||
|
||||
|
||||
:PROPERTIES:
|
||||
:FOO: bar
|
||||
:END:
|
||||
@@ -1,12 +0,0 @@
|
||||
|
||||
|
||||
|
||||
|
||||
# Blank lines and comments can come before property drawers in the zeroth section
|
||||
|
||||
|
||||
|
||||
|
||||
:PROPERTIES:
|
||||
:FOO: bar
|
||||
:END:
|
||||
@@ -0,0 +1,63 @@
|
||||
#+NAME: a
|
||||
#+caption: b *lorem* ipsum
|
||||
#+results: c
|
||||
#+headers: d
|
||||
#+header: e
|
||||
#+label: f
|
||||
#+plot: g
|
||||
#+resname: h
|
||||
#+result: i
|
||||
#+source: j
|
||||
#+srcname: k
|
||||
#+tblname: l
|
||||
#+attr_latex: m
|
||||
#+attr_html: n
|
||||
#+NAME: aa
|
||||
#+caption: bb *lorem* ipsum
|
||||
#+results: cc
|
||||
#+headers: dd
|
||||
#+header: ee
|
||||
#+label: ff
|
||||
#+plot: gg
|
||||
#+resname: hh
|
||||
#+result: ii
|
||||
#+source: jj
|
||||
#+srcname: kk
|
||||
#+tblname: ll
|
||||
#+attr_latex: mm
|
||||
#+attr_html: nn
|
||||
| foo | bar |
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
#+NAME: A
|
||||
#+CAPTION: B *LOREM* IPSUM
|
||||
#+RESULTS: C
|
||||
#+HEADERS: D
|
||||
#+HEADER: E
|
||||
#+LABEL: F
|
||||
#+PLOT: G
|
||||
#+RESNAME: H
|
||||
#+RESULT: I
|
||||
#+SOURCE: J
|
||||
#+SRCNAME: K
|
||||
#+TBLNAME: L
|
||||
#+ATTR_LATEX: M
|
||||
#+ATTR_HTML: N
|
||||
#+NAME: AA
|
||||
#+CAPTION: BB *LOREM* IPSUM
|
||||
#+RESULTS: CC
|
||||
#+HEADERS: DD
|
||||
#+HEADER: EE
|
||||
#+LABEL: FF
|
||||
#+PLOT: GG
|
||||
#+RESNAME: HH
|
||||
#+RESULT: II
|
||||
#+SOURCE: JJ
|
||||
#+SRCNAME: KK
|
||||
#+TBLNAME: LL
|
||||
#+ATTR_LATEX: MM
|
||||
#+ATTR_HTML: NN
|
||||
| foo | bar |
|
||||
@@ -1,2 +0,0 @@
|
||||
#+NAME: foo
|
||||
| foo | bar |
|
||||
3
org_mode_samples/greater_element/table/row/name.org
Normal file
3
org_mode_samples/greater_element/table/row/name.org
Normal file
@@ -0,0 +1,3 @@
|
||||
|foo|
|
||||
#+NAME: bar
|
||||
|baz |
|
||||
@@ -0,0 +1,63 @@
|
||||
#+NAME: a
|
||||
#+caption: b *lorem* ipsum
|
||||
#+results: c
|
||||
#+headers: d
|
||||
#+header: e
|
||||
#+label: f
|
||||
#+plot: g
|
||||
#+resname: h
|
||||
#+result: i
|
||||
#+source: j
|
||||
#+srcname: k
|
||||
#+tblname: l
|
||||
#+attr_latex: m
|
||||
#+attr_html: n
|
||||
#+NAME: aa
|
||||
#+caption: bb *lorem* ipsum
|
||||
#+results: cc
|
||||
#+headers: dd
|
||||
#+header: ee
|
||||
#+label: ff
|
||||
#+plot: gg
|
||||
#+resname: hh
|
||||
#+result: ii
|
||||
#+source: jj
|
||||
#+srcname: kk
|
||||
#+tblname: ll
|
||||
#+attr_latex: mm
|
||||
#+attr_html: nn
|
||||
#+call: foo(bar="baz")
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
#+NAME: A
|
||||
#+CAPTION: B *LOREM* IPSUM
|
||||
#+RESULTS: C
|
||||
#+HEADERS: D
|
||||
#+HEADER: E
|
||||
#+LABEL: F
|
||||
#+PLOT: G
|
||||
#+RESNAME: H
|
||||
#+RESULT: I
|
||||
#+SOURCE: J
|
||||
#+SRCNAME: K
|
||||
#+TBLNAME: L
|
||||
#+ATTR_LATEX: M
|
||||
#+ATTR_HTML: N
|
||||
#+NAME: AA
|
||||
#+CAPTION: BB *LOREM* IPSUM
|
||||
#+RESULTS: CC
|
||||
#+HEADERS: DD
|
||||
#+HEADER: EE
|
||||
#+LABEL: FF
|
||||
#+PLOT: GG
|
||||
#+RESNAME: HH
|
||||
#+RESULT: II
|
||||
#+SOURCE: JJ
|
||||
#+SRCNAME: KK
|
||||
#+TBLNAME: LL
|
||||
#+ATTR_LATEX: MM
|
||||
#+ATTR_HTML: NN
|
||||
#+call: foo(bar="baz")
|
||||
@@ -1,2 +0,0 @@
|
||||
#+NAME: foo
|
||||
#+call: foo(bar="baz")
|
||||
@@ -0,0 +1,63 @@
|
||||
#+NAME: a
|
||||
#+caption: b *lorem* ipsum
|
||||
#+results: c
|
||||
#+headers: d
|
||||
#+header: e
|
||||
#+label: f
|
||||
#+plot: g
|
||||
#+resname: h
|
||||
#+result: i
|
||||
#+source: j
|
||||
#+srcname: k
|
||||
#+tblname: l
|
||||
#+attr_latex: m
|
||||
#+attr_html: n
|
||||
#+NAME: aa
|
||||
#+caption: bb *lorem* ipsum
|
||||
#+results: cc
|
||||
#+headers: dd
|
||||
#+header: ee
|
||||
#+label: ff
|
||||
#+plot: gg
|
||||
#+resname: hh
|
||||
#+result: ii
|
||||
#+source: jj
|
||||
#+srcname: kk
|
||||
#+tblname: ll
|
||||
#+attr_latex: mm
|
||||
#+attr_html: nn
|
||||
%%(foo)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
#+NAME: A
|
||||
#+CAPTION: B *LOREM* IPSUM
|
||||
#+RESULTS: C
|
||||
#+HEADERS: D
|
||||
#+HEADER: E
|
||||
#+LABEL: F
|
||||
#+PLOT: G
|
||||
#+RESNAME: H
|
||||
#+RESULT: I
|
||||
#+SOURCE: J
|
||||
#+SRCNAME: K
|
||||
#+TBLNAME: L
|
||||
#+ATTR_LATEX: M
|
||||
#+ATTR_HTML: N
|
||||
#+NAME: AA
|
||||
#+CAPTION: BB *LOREM* IPSUM
|
||||
#+RESULTS: CC
|
||||
#+HEADERS: DD
|
||||
#+HEADER: EE
|
||||
#+LABEL: FF
|
||||
#+PLOT: GG
|
||||
#+RESNAME: HH
|
||||
#+RESULT: II
|
||||
#+SOURCE: JJ
|
||||
#+SRCNAME: KK
|
||||
#+TBLNAME: LL
|
||||
#+ATTR_LATEX: MM
|
||||
#+ATTR_HTML: NN
|
||||
%%(foo)
|
||||
@@ -1,2 +0,0 @@
|
||||
#+NAME: foo
|
||||
%%(foo)
|
||||
@@ -0,0 +1,63 @@
|
||||
#+NAME: a
|
||||
#+caption: b *lorem* ipsum
|
||||
#+results: c
|
||||
#+headers: d
|
||||
#+header: e
|
||||
#+label: f
|
||||
#+plot: g
|
||||
#+resname: h
|
||||
#+result: i
|
||||
#+source: j
|
||||
#+srcname: k
|
||||
#+tblname: l
|
||||
#+attr_latex: m
|
||||
#+attr_html: n
|
||||
#+NAME: aa
|
||||
#+caption: bb *lorem* ipsum
|
||||
#+results: cc
|
||||
#+headers: dd
|
||||
#+header: ee
|
||||
#+label: ff
|
||||
#+plot: gg
|
||||
#+resname: hh
|
||||
#+result: ii
|
||||
#+source: jj
|
||||
#+srcname: kk
|
||||
#+tblname: ll
|
||||
#+attr_latex: mm
|
||||
#+attr_html: nn
|
||||
: bar
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
#+NAME: A
|
||||
#+CAPTION: B *LOREM* IPSUM
|
||||
#+RESULTS: C
|
||||
#+HEADERS: D
|
||||
#+HEADER: E
|
||||
#+LABEL: F
|
||||
#+PLOT: G
|
||||
#+RESNAME: H
|
||||
#+RESULT: I
|
||||
#+SOURCE: J
|
||||
#+SRCNAME: K
|
||||
#+TBLNAME: L
|
||||
#+ATTR_LATEX: M
|
||||
#+ATTR_HTML: N
|
||||
#+NAME: AA
|
||||
#+CAPTION: BB *LOREM* IPSUM
|
||||
#+RESULTS: CC
|
||||
#+HEADERS: DD
|
||||
#+HEADER: EE
|
||||
#+LABEL: FF
|
||||
#+PLOT: GG
|
||||
#+RESNAME: HH
|
||||
#+RESULT: II
|
||||
#+SOURCE: JJ
|
||||
#+SRCNAME: KK
|
||||
#+TBLNAME: LL
|
||||
#+ATTR_LATEX: MM
|
||||
#+ATTR_HTML: NN
|
||||
: bar
|
||||
@@ -1,2 +0,0 @@
|
||||
#+NAME: foo
|
||||
: bar
|
||||
@@ -0,0 +1,63 @@
|
||||
#+NAME: a
|
||||
#+caption: b *lorem* ipsum
|
||||
#+results: c
|
||||
#+headers: d
|
||||
#+header: e
|
||||
#+label: f
|
||||
#+plot: g
|
||||
#+resname: h
|
||||
#+result: i
|
||||
#+source: j
|
||||
#+srcname: k
|
||||
#+tblname: l
|
||||
#+attr_latex: m
|
||||
#+attr_html: n
|
||||
#+NAME: aa
|
||||
#+caption: bb *lorem* ipsum
|
||||
#+results: cc
|
||||
#+headers: dd
|
||||
#+header: ee
|
||||
#+label: ff
|
||||
#+plot: gg
|
||||
#+resname: hh
|
||||
#+result: ii
|
||||
#+source: jj
|
||||
#+srcname: kk
|
||||
#+tblname: ll
|
||||
#+attr_latex: mm
|
||||
#+attr_html: nn
|
||||
-----
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
#+NAME: A
|
||||
#+CAPTION: B *LOREM* IPSUM
|
||||
#+RESULTS: C
|
||||
#+HEADERS: D
|
||||
#+HEADER: E
|
||||
#+LABEL: F
|
||||
#+PLOT: G
|
||||
#+RESNAME: H
|
||||
#+RESULT: I
|
||||
#+SOURCE: J
|
||||
#+SRCNAME: K
|
||||
#+TBLNAME: L
|
||||
#+ATTR_LATEX: M
|
||||
#+ATTR_HTML: N
|
||||
#+NAME: AA
|
||||
#+CAPTION: BB *LOREM* IPSUM
|
||||
#+RESULTS: CC
|
||||
#+HEADERS: DD
|
||||
#+HEADER: EE
|
||||
#+LABEL: FF
|
||||
#+PLOT: GG
|
||||
#+RESNAME: HH
|
||||
#+RESULT: II
|
||||
#+SOURCE: JJ
|
||||
#+SRCNAME: KK
|
||||
#+TBLNAME: LL
|
||||
#+ATTR_LATEX: MM
|
||||
#+ATTR_HTML: NN
|
||||
-----
|
||||
@@ -1,2 +0,0 @@
|
||||
#+NAME: foo
|
||||
-----
|
||||
@@ -1,8 +1,63 @@
|
||||
#+name: foo
|
||||
#+caption: bar
|
||||
#+caption: baz
|
||||
[[file:lorem/ipsum.png]]
|
||||
#+NAME: a
|
||||
#+caption: b *lorem* ipsum
|
||||
#+results: c
|
||||
#+headers: d
|
||||
#+header: e
|
||||
#+label: f
|
||||
#+plot: g
|
||||
#+resname: h
|
||||
#+result: i
|
||||
#+source: j
|
||||
#+srcname: k
|
||||
#+tblname: l
|
||||
#+attr_latex: m
|
||||
#+attr_html: n
|
||||
#+NAME: aa
|
||||
#+caption: bb *lorem* ipsum
|
||||
#+results: cc
|
||||
#+headers: dd
|
||||
#+header: ee
|
||||
#+label: ff
|
||||
#+plot: gg
|
||||
#+resname: hh
|
||||
#+result: ii
|
||||
#+source: jj
|
||||
#+srcname: kk
|
||||
#+tblname: ll
|
||||
#+attr_latex: mm
|
||||
#+attr_html: nn
|
||||
#+foo: bar
|
||||
|
||||
#+name: cat
|
||||
#+caption: dog
|
||||
[[file:lorem/ipsum.png]]
|
||||
|
||||
|
||||
|
||||
|
||||
#+NAME: A
|
||||
#+CAPTION: B *LOREM* IPSUM
|
||||
#+RESULTS: C
|
||||
#+HEADERS: D
|
||||
#+HEADER: E
|
||||
#+LABEL: F
|
||||
#+PLOT: G
|
||||
#+RESNAME: H
|
||||
#+RESULT: I
|
||||
#+SOURCE: J
|
||||
#+SRCNAME: K
|
||||
#+TBLNAME: L
|
||||
#+ATTR_LATEX: M
|
||||
#+ATTR_HTML: N
|
||||
#+NAME: AA
|
||||
#+CAPTION: BB *LOREM* IPSUM
|
||||
#+RESULTS: CC
|
||||
#+HEADERS: DD
|
||||
#+HEADER: EE
|
||||
#+LABEL: FF
|
||||
#+PLOT: GG
|
||||
#+RESNAME: HH
|
||||
#+RESULT: II
|
||||
#+SOURCE: JJ
|
||||
#+SRCNAME: KK
|
||||
#+TBLNAME: LL
|
||||
#+ATTR_LATEX: MM
|
||||
#+ATTR_HTML: NN
|
||||
#+foo: bar
|
||||
|
||||
@@ -1,7 +0,0 @@
|
||||
# This test is to prove that the parser works with affiliated keywords that have both a shorter and longer version.
|
||||
|
||||
#+results:
|
||||
#+result:
|
||||
#+begin_latex
|
||||
\foo
|
||||
#+end_latex
|
||||
@@ -1,2 +0,0 @@
|
||||
#+NAME: foo
|
||||
#+FOO: BAR
|
||||
@@ -0,0 +1,67 @@
|
||||
#+NAME: a
|
||||
#+caption: b *lorem* ipsum
|
||||
#+results: c
|
||||
#+headers: d
|
||||
#+header: e
|
||||
#+label: f
|
||||
#+plot: g
|
||||
#+resname: h
|
||||
#+result: i
|
||||
#+source: j
|
||||
#+srcname: k
|
||||
#+tblname: l
|
||||
#+attr_latex: m
|
||||
#+attr_html: n
|
||||
#+NAME: aa
|
||||
#+caption: bb *lorem* ipsum
|
||||
#+results: cc
|
||||
#+headers: dd
|
||||
#+header: ee
|
||||
#+label: ff
|
||||
#+plot: gg
|
||||
#+resname: hh
|
||||
#+result: ii
|
||||
#+source: jj
|
||||
#+srcname: kk
|
||||
#+tblname: ll
|
||||
#+attr_latex: mm
|
||||
#+attr_html: nn
|
||||
\begin{foo}
|
||||
bar
|
||||
\end{foo}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
#+NAME: A
|
||||
#+CAPTION: B *LOREM* IPSUM
|
||||
#+RESULTS: C
|
||||
#+HEADERS: D
|
||||
#+HEADER: E
|
||||
#+LABEL: F
|
||||
#+PLOT: G
|
||||
#+RESNAME: H
|
||||
#+RESULT: I
|
||||
#+SOURCE: J
|
||||
#+SRCNAME: K
|
||||
#+TBLNAME: L
|
||||
#+ATTR_LATEX: M
|
||||
#+ATTR_HTML: N
|
||||
#+NAME: AA
|
||||
#+CAPTION: BB *LOREM* IPSUM
|
||||
#+RESULTS: CC
|
||||
#+HEADERS: DD
|
||||
#+HEADER: EE
|
||||
#+LABEL: FF
|
||||
#+PLOT: GG
|
||||
#+RESNAME: HH
|
||||
#+RESULT: II
|
||||
#+SOURCE: JJ
|
||||
#+SRCNAME: KK
|
||||
#+TBLNAME: LL
|
||||
#+ATTR_LATEX: MM
|
||||
#+ATTR_HTML: NN
|
||||
\begin{foo}
|
||||
bar
|
||||
\end{foo}
|
||||
@@ -1,4 +0,0 @@
|
||||
#+NAME: foo
|
||||
\begin{foo}
|
||||
bar
|
||||
\end{foo}
|
||||
@@ -0,0 +1,67 @@
|
||||
#+NAME: a
|
||||
#+caption: b *lorem* ipsum
|
||||
#+results: c
|
||||
#+headers: d
|
||||
#+header: e
|
||||
#+label: f
|
||||
#+plot: g
|
||||
#+resname: h
|
||||
#+result: i
|
||||
#+source: j
|
||||
#+srcname: k
|
||||
#+tblname: l
|
||||
#+attr_latex: m
|
||||
#+attr_html: n
|
||||
#+NAME: aa
|
||||
#+caption: bb *lorem* ipsum
|
||||
#+results: cc
|
||||
#+headers: dd
|
||||
#+header: ee
|
||||
#+label: ff
|
||||
#+plot: gg
|
||||
#+resname: hh
|
||||
#+result: ii
|
||||
#+source: jj
|
||||
#+srcname: kk
|
||||
#+tblname: ll
|
||||
#+attr_latex: mm
|
||||
#+attr_html: nn
|
||||
#+begin_comment text
|
||||
bar
|
||||
#+end_comment
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
#+NAME: A
|
||||
#+CAPTION: B *LOREM* IPSUM
|
||||
#+RESULTS: C
|
||||
#+HEADERS: D
|
||||
#+HEADER: E
|
||||
#+LABEL: F
|
||||
#+PLOT: G
|
||||
#+RESNAME: H
|
||||
#+RESULT: I
|
||||
#+SOURCE: J
|
||||
#+SRCNAME: K
|
||||
#+TBLNAME: L
|
||||
#+ATTR_LATEX: M
|
||||
#+ATTR_HTML: N
|
||||
#+NAME: AA
|
||||
#+CAPTION: BB *LOREM* IPSUM
|
||||
#+RESULTS: CC
|
||||
#+HEADERS: DD
|
||||
#+HEADER: EE
|
||||
#+LABEL: FF
|
||||
#+PLOT: GG
|
||||
#+RESNAME: HH
|
||||
#+RESULT: II
|
||||
#+SOURCE: JJ
|
||||
#+SRCNAME: KK
|
||||
#+TBLNAME: LL
|
||||
#+ATTR_LATEX: MM
|
||||
#+ATTR_HTML: NN
|
||||
#+begin_comment text
|
||||
bar
|
||||
#+end_comment
|
||||
@@ -1,4 +0,0 @@
|
||||
#+NAME: foo
|
||||
#+begin_comment text
|
||||
bar
|
||||
#+end_comment
|
||||
@@ -0,0 +1,67 @@
|
||||
#+NAME: a
|
||||
#+caption: b *lorem* ipsum
|
||||
#+results: c
|
||||
#+headers: d
|
||||
#+header: e
|
||||
#+label: f
|
||||
#+plot: g
|
||||
#+resname: h
|
||||
#+result: i
|
||||
#+source: j
|
||||
#+srcname: k
|
||||
#+tblname: l
|
||||
#+attr_latex: m
|
||||
#+attr_html: n
|
||||
#+NAME: aa
|
||||
#+caption: bb *lorem* ipsum
|
||||
#+results: cc
|
||||
#+headers: dd
|
||||
#+header: ee
|
||||
#+label: ff
|
||||
#+plot: gg
|
||||
#+resname: hh
|
||||
#+result: ii
|
||||
#+source: jj
|
||||
#+srcname: kk
|
||||
#+tblname: ll
|
||||
#+attr_latex: mm
|
||||
#+attr_html: nn
|
||||
#+begin_example text
|
||||
bar
|
||||
#+end_example
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
#+NAME: A
|
||||
#+CAPTION: B *LOREM* IPSUM
|
||||
#+RESULTS: C
|
||||
#+HEADERS: D
|
||||
#+HEADER: E
|
||||
#+LABEL: F
|
||||
#+PLOT: G
|
||||
#+RESNAME: H
|
||||
#+RESULT: I
|
||||
#+SOURCE: J
|
||||
#+SRCNAME: K
|
||||
#+TBLNAME: L
|
||||
#+ATTR_LATEX: M
|
||||
#+ATTR_HTML: N
|
||||
#+NAME: AA
|
||||
#+CAPTION: BB *LOREM* IPSUM
|
||||
#+RESULTS: CC
|
||||
#+HEADERS: DD
|
||||
#+HEADER: EE
|
||||
#+LABEL: FF
|
||||
#+PLOT: GG
|
||||
#+RESNAME: HH
|
||||
#+RESULT: II
|
||||
#+SOURCE: JJ
|
||||
#+SRCNAME: KK
|
||||
#+TBLNAME: LL
|
||||
#+ATTR_LATEX: MM
|
||||
#+ATTR_HTML: NN
|
||||
#+begin_example text
|
||||
bar
|
||||
#+end_example
|
||||
@@ -1,4 +0,0 @@
|
||||
#+NAME: foo
|
||||
#+begin_example text
|
||||
bar
|
||||
#+end_example
|
||||
@@ -0,0 +1,67 @@
|
||||
#+NAME: a
|
||||
#+caption: b *lorem* ipsum
|
||||
#+results: c
|
||||
#+headers: d
|
||||
#+header: e
|
||||
#+label: f
|
||||
#+plot: g
|
||||
#+resname: h
|
||||
#+result: i
|
||||
#+source: j
|
||||
#+srcname: k
|
||||
#+tblname: l
|
||||
#+attr_latex: m
|
||||
#+attr_html: n
|
||||
#+NAME: aa
|
||||
#+caption: bb *lorem* ipsum
|
||||
#+results: cc
|
||||
#+headers: dd
|
||||
#+header: ee
|
||||
#+label: ff
|
||||
#+plot: gg
|
||||
#+resname: hh
|
||||
#+result: ii
|
||||
#+source: jj
|
||||
#+srcname: kk
|
||||
#+tblname: ll
|
||||
#+attr_latex: mm
|
||||
#+attr_html: nn
|
||||
#+begin_export text
|
||||
bar
|
||||
#+end_export
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
#+NAME: A
|
||||
#+CAPTION: B *LOREM* IPSUM
|
||||
#+RESULTS: C
|
||||
#+HEADERS: D
|
||||
#+HEADER: E
|
||||
#+LABEL: F
|
||||
#+PLOT: G
|
||||
#+RESNAME: H
|
||||
#+RESULT: I
|
||||
#+SOURCE: J
|
||||
#+SRCNAME: K
|
||||
#+TBLNAME: L
|
||||
#+ATTR_LATEX: M
|
||||
#+ATTR_HTML: N
|
||||
#+NAME: AA
|
||||
#+CAPTION: BB *LOREM* IPSUM
|
||||
#+RESULTS: CC
|
||||
#+HEADERS: DD
|
||||
#+HEADER: EE
|
||||
#+LABEL: FF
|
||||
#+PLOT: GG
|
||||
#+RESNAME: HH
|
||||
#+RESULT: II
|
||||
#+SOURCE: JJ
|
||||
#+SRCNAME: KK
|
||||
#+TBLNAME: LL
|
||||
#+ATTR_LATEX: MM
|
||||
#+ATTR_HTML: NN
|
||||
#+begin_export text
|
||||
bar
|
||||
#+end_export
|
||||
@@ -1,4 +0,0 @@
|
||||
#+NAME: foo
|
||||
#+begin_export text
|
||||
bar
|
||||
#+end_export
|
||||
@@ -0,0 +1,67 @@
|
||||
#+NAME: a
|
||||
#+caption: b *lorem* ipsum
|
||||
#+results: c
|
||||
#+headers: d
|
||||
#+header: e
|
||||
#+label: f
|
||||
#+plot: g
|
||||
#+resname: h
|
||||
#+result: i
|
||||
#+source: j
|
||||
#+srcname: k
|
||||
#+tblname: l
|
||||
#+attr_latex: m
|
||||
#+attr_html: n
|
||||
#+NAME: aa
|
||||
#+caption: bb *lorem* ipsum
|
||||
#+results: cc
|
||||
#+headers: dd
|
||||
#+header: ee
|
||||
#+label: ff
|
||||
#+plot: gg
|
||||
#+resname: hh
|
||||
#+result: ii
|
||||
#+source: jj
|
||||
#+srcname: kk
|
||||
#+tblname: ll
|
||||
#+attr_latex: mm
|
||||
#+attr_html: nn
|
||||
#+begin_src text
|
||||
bar
|
||||
#+end_src
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
#+NAME: A
|
||||
#+CAPTION: B *LOREM* IPSUM
|
||||
#+RESULTS: C
|
||||
#+HEADERS: D
|
||||
#+HEADER: E
|
||||
#+LABEL: F
|
||||
#+PLOT: G
|
||||
#+RESNAME: H
|
||||
#+RESULT: I
|
||||
#+SOURCE: J
|
||||
#+SRCNAME: K
|
||||
#+TBLNAME: L
|
||||
#+ATTR_LATEX: M
|
||||
#+ATTR_HTML: N
|
||||
#+NAME: AA
|
||||
#+CAPTION: BB *LOREM* IPSUM
|
||||
#+RESULTS: CC
|
||||
#+HEADERS: DD
|
||||
#+HEADER: EE
|
||||
#+LABEL: FF
|
||||
#+PLOT: GG
|
||||
#+RESNAME: HH
|
||||
#+RESULT: II
|
||||
#+SOURCE: JJ
|
||||
#+SRCNAME: KK
|
||||
#+TBLNAME: LL
|
||||
#+ATTR_LATEX: MM
|
||||
#+ATTR_HTML: NN
|
||||
#+begin_src text
|
||||
bar
|
||||
#+end_src
|
||||
@@ -1,4 +0,0 @@
|
||||
#+NAME: foo
|
||||
#+begin_src text
|
||||
bar
|
||||
#+end_src
|
||||
@@ -0,0 +1,67 @@
|
||||
#+NAME: a
|
||||
#+caption: b *lorem* ipsum
|
||||
#+results: c
|
||||
#+headers: d
|
||||
#+header: e
|
||||
#+label: f
|
||||
#+plot: g
|
||||
#+resname: h
|
||||
#+result: i
|
||||
#+source: j
|
||||
#+srcname: k
|
||||
#+tblname: l
|
||||
#+attr_latex: m
|
||||
#+attr_html: n
|
||||
#+NAME: aa
|
||||
#+caption: bb *lorem* ipsum
|
||||
#+results: cc
|
||||
#+headers: dd
|
||||
#+header: ee
|
||||
#+label: ff
|
||||
#+plot: gg
|
||||
#+resname: hh
|
||||
#+result: ii
|
||||
#+source: jj
|
||||
#+srcname: kk
|
||||
#+tblname: ll
|
||||
#+attr_latex: mm
|
||||
#+attr_html: nn
|
||||
#+begin_verse text
|
||||
bar
|
||||
#+end_verse
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
#+NAME: A
|
||||
#+CAPTION: B *LOREM* IPSUM
|
||||
#+RESULTS: C
|
||||
#+HEADERS: D
|
||||
#+HEADER: E
|
||||
#+LABEL: F
|
||||
#+PLOT: G
|
||||
#+RESNAME: H
|
||||
#+RESULT: I
|
||||
#+SOURCE: J
|
||||
#+SRCNAME: K
|
||||
#+TBLNAME: L
|
||||
#+ATTR_LATEX: M
|
||||
#+ATTR_HTML: N
|
||||
#+NAME: AA
|
||||
#+CAPTION: BB *LOREM* IPSUM
|
||||
#+RESULTS: CC
|
||||
#+HEADERS: DD
|
||||
#+HEADER: EE
|
||||
#+LABEL: FF
|
||||
#+PLOT: GG
|
||||
#+RESNAME: HH
|
||||
#+RESULT: II
|
||||
#+SOURCE: JJ
|
||||
#+SRCNAME: KK
|
||||
#+TBLNAME: LL
|
||||
#+ATTR_LATEX: MM
|
||||
#+ATTR_HTML: NN
|
||||
#+begin_verse text
|
||||
bar
|
||||
#+end_verse
|
||||
@@ -1,4 +0,0 @@
|
||||
#+NAME: foo
|
||||
#+begin_verse text
|
||||
bar
|
||||
#+end_verse
|
||||
@@ -0,0 +1,63 @@
|
||||
#+NAME: a
|
||||
#+caption: b *lorem* ipsum
|
||||
#+results: c
|
||||
#+headers: d
|
||||
#+header: e
|
||||
#+label: f
|
||||
#+plot: g
|
||||
#+resname: h
|
||||
#+result: i
|
||||
#+source: j
|
||||
#+srcname: k
|
||||
#+tblname: l
|
||||
#+attr_latex: m
|
||||
#+attr_html: n
|
||||
#+NAME: aa
|
||||
#+caption: bb *lorem* ipsum
|
||||
#+results: cc
|
||||
#+headers: dd
|
||||
#+header: ee
|
||||
#+label: ff
|
||||
#+plot: gg
|
||||
#+resname: hh
|
||||
#+result: ii
|
||||
#+source: jj
|
||||
#+srcname: kk
|
||||
#+tblname: ll
|
||||
#+attr_latex: mm
|
||||
#+attr_html: nn
|
||||
bar
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
#+NAME: A
|
||||
#+CAPTION: B *LOREM* IPSUM
|
||||
#+RESULTS: C
|
||||
#+HEADERS: D
|
||||
#+HEADER: E
|
||||
#+LABEL: F
|
||||
#+PLOT: G
|
||||
#+RESNAME: H
|
||||
#+RESULT: I
|
||||
#+SOURCE: J
|
||||
#+SRCNAME: K
|
||||
#+TBLNAME: L
|
||||
#+ATTR_LATEX: M
|
||||
#+ATTR_HTML: N
|
||||
#+NAME: AA
|
||||
#+CAPTION: BB *LOREM* IPSUM
|
||||
#+RESULTS: CC
|
||||
#+HEADERS: DD
|
||||
#+HEADER: EE
|
||||
#+LABEL: FF
|
||||
#+PLOT: GG
|
||||
#+RESNAME: HH
|
||||
#+RESULT: II
|
||||
#+SOURCE: JJ
|
||||
#+SRCNAME: KK
|
||||
#+TBLNAME: LL
|
||||
#+ATTR_LATEX: MM
|
||||
#+ATTR_HTML: NN
|
||||
bar
|
||||
@@ -1,2 +0,0 @@
|
||||
#+NAME: foo
|
||||
bar
|
||||
3
org_mode_samples/sections_and_headings/name.org
Normal file
3
org_mode_samples/sections_and_headings/name.org
Normal file
@@ -0,0 +1,3 @@
|
||||
#+NAME: foo
|
||||
* bar
|
||||
#+NAME: baz
|
||||
@@ -14,7 +14,12 @@ mod init_tracing;
|
||||
|
||||
#[cfg(not(feature = "tracing"))]
|
||||
fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
main_body()
|
||||
let rt = tokio::runtime::Runtime::new()?;
|
||||
let result = rt.block_on(async {
|
||||
let main_body_result = main_body().await;
|
||||
main_body_result
|
||||
});
|
||||
result
|
||||
}
|
||||
|
||||
#[cfg(feature = "tracing")]
|
||||
@@ -22,7 +27,7 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
let rt = tokio::runtime::Runtime::new()?;
|
||||
let result = rt.block_on(async {
|
||||
init_telemetry()?;
|
||||
let main_body_result = main_body();
|
||||
let main_body_result = main_body().await;
|
||||
shutdown_telemetry()?;
|
||||
main_body_result
|
||||
});
|
||||
@@ -30,14 +35,21 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
}
|
||||
|
||||
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
|
||||
fn main_body() -> Result<(), Box<dyn std::error::Error>> {
|
||||
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()?;
|
||||
run_anonymous_compare(org_contents)
|
||||
if run_anonymous_compare(org_contents).await? {
|
||||
} else {
|
||||
Err("Diff results do not match.")?;
|
||||
}
|
||||
Ok(())
|
||||
} else {
|
||||
for arg in args {
|
||||
run_compare_on_file(arg)?
|
||||
if run_compare_on_file(arg).await? {
|
||||
} else {
|
||||
Err("Diff results do not match.")?;
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
399
src/bin_foreign_document_test.rs
Normal file
399
src/bin_foreign_document_test.rs
Normal file
@@ -0,0 +1,399 @@
|
||||
#![feature(round_char_boundary)]
|
||||
#![feature(exact_size_is_empty)]
|
||||
use std::path::Path;
|
||||
use std::path::PathBuf;
|
||||
use std::process::ExitCode;
|
||||
|
||||
use futures::future::BoxFuture;
|
||||
use futures::future::FutureExt;
|
||||
use organic::compare::silent_compare_on_file;
|
||||
use tokio::sync::Semaphore;
|
||||
use tokio::task::JoinError;
|
||||
use walkdir::WalkDir;
|
||||
|
||||
#[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<ExitCode, Box<dyn std::error::Error>> {
|
||||
let rt = tokio::runtime::Runtime::new()?;
|
||||
let result = rt.block_on(async {
|
||||
let main_body_result = main_body().await;
|
||||
main_body_result
|
||||
});
|
||||
result
|
||||
}
|
||||
|
||||
#[cfg(feature = "tracing")]
|
||||
fn main() -> Result<ExitCode, Box<dyn std::error::Error>> {
|
||||
let rt = tokio::runtime::Runtime::new()?;
|
||||
let result = rt.block_on(async {
|
||||
init_telemetry()?;
|
||||
let main_body_result = main_body().await;
|
||||
shutdown_telemetry()?;
|
||||
main_body_result
|
||||
});
|
||||
result
|
||||
}
|
||||
|
||||
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
|
||||
async fn main_body() -> Result<ExitCode, Box<dyn std::error::Error>> {
|
||||
let layer = compare_group("org-mode", || {
|
||||
compare_all_org_document("/foreign_documents/org-mode")
|
||||
});
|
||||
let layer = layer.chain(compare_group("emacs", || {
|
||||
compare_all_org_document("/foreign_documents/emacs")
|
||||
}));
|
||||
let layer = layer.chain(compare_group("worg", || {
|
||||
compare_all_org_document("/foreign_documents/worg")
|
||||
}));
|
||||
let layer = layer.chain(compare_group("howard_abrams", compare_howard_abrams));
|
||||
let layer = layer.chain(compare_group("doomemacs", || {
|
||||
compare_all_org_document("/foreign_documents/doomemacs")
|
||||
}));
|
||||
|
||||
let running_tests: Vec<_> = layer.map(|c| tokio::spawn(c.run_test())).collect();
|
||||
let mut any_failed = false;
|
||||
for test in running_tests.into_iter() {
|
||||
let test_result = test.await??;
|
||||
if test_result.is_immediately_bad() || test_result.has_bad_children() {
|
||||
any_failed = true;
|
||||
}
|
||||
test_result.print();
|
||||
}
|
||||
|
||||
if any_failed {
|
||||
println!(
|
||||
"{color}Some tests failed.{reset}",
|
||||
color = TestResult::foreground_color(255, 0, 0),
|
||||
reset = TestResult::reset_color(),
|
||||
);
|
||||
Ok(ExitCode::FAILURE)
|
||||
} else {
|
||||
println!(
|
||||
"{color}All tests passed.{reset}",
|
||||
color = TestResult::foreground_color(0, 255, 0),
|
||||
reset = TestResult::reset_color(),
|
||||
);
|
||||
Ok(ExitCode::SUCCESS)
|
||||
}
|
||||
}
|
||||
|
||||
fn compare_howard_abrams() -> impl Iterator<Item = TestConfig> {
|
||||
let layer = compare_group("dot-files", || {
|
||||
compare_all_org_document("/foreign_documents/howardabrams/dot-files")
|
||||
});
|
||||
let layer = layer.chain(compare_group("hamacs", || {
|
||||
compare_all_org_document("/foreign_documents/howardabrams/hamacs")
|
||||
}));
|
||||
let layer = layer.chain(compare_group("demo-it", || {
|
||||
compare_all_org_document("/foreign_documents/howardabrams/demo-it")
|
||||
}));
|
||||
let layer = layer.chain(compare_group("magit-demo", || {
|
||||
compare_all_org_document("/foreign_documents/howardabrams/magit-demo")
|
||||
}));
|
||||
let layer = layer.chain(compare_group("pdx-emacs-hackers", || {
|
||||
compare_all_org_document("/foreign_documents/howardabrams/pdx-emacs-hackers")
|
||||
}));
|
||||
let layer = layer.chain(compare_group("flora-simulator", || {
|
||||
compare_all_org_document("/foreign_documents/howardabrams/flora-simulator")
|
||||
}));
|
||||
let layer = layer.chain(compare_group("literate-devops-demo", || {
|
||||
compare_all_org_document("/foreign_documents/howardabrams/literate-devops-demo")
|
||||
}));
|
||||
let layer = layer.chain(compare_group("clojure-yesql-xp", || {
|
||||
compare_all_org_document("/foreign_documents/howardabrams/clojure-yesql-xp")
|
||||
}));
|
||||
let layer = layer.chain(compare_group("veep", || {
|
||||
compare_all_org_document("/foreign_documents/howardabrams/veep")
|
||||
}));
|
||||
layer
|
||||
}
|
||||
|
||||
fn compare_group<N: Into<String>, F: Fn() -> I, I: Iterator<Item = TestConfig>>(
|
||||
name: N,
|
||||
inner: F,
|
||||
) -> impl Iterator<Item = TestConfig> {
|
||||
std::iter::once(TestConfig::TestLayer(TestLayer {
|
||||
name: name.into(),
|
||||
children: inner().collect(),
|
||||
}))
|
||||
}
|
||||
|
||||
fn compare_all_org_document<P: AsRef<Path>>(root_dir: P) -> impl Iterator<Item = TestConfig> {
|
||||
let root_dir = root_dir.as_ref();
|
||||
let mut test_files = WalkDir::new(root_dir)
|
||||
.into_iter()
|
||||
.filter(|e| match e {
|
||||
Ok(dir_entry) => {
|
||||
dir_entry.file_type().is_file()
|
||||
&& Path::new(dir_entry.file_name())
|
||||
.extension()
|
||||
.map(|ext| ext.to_ascii_lowercase() == "org")
|
||||
.unwrap_or(false)
|
||||
}
|
||||
Err(_) => true,
|
||||
})
|
||||
.collect::<Result<Vec<_>, _>>()
|
||||
.unwrap();
|
||||
test_files.sort_by_cached_key(|test_file| PathBuf::from(test_file.path()));
|
||||
let test_configs: Vec<_> = test_files
|
||||
.into_iter()
|
||||
.map(|test_file| {
|
||||
let name = test_file
|
||||
.path()
|
||||
.strip_prefix(root_dir)
|
||||
.expect("Result is from walkdir so it must be below the root directory.")
|
||||
.as_os_str()
|
||||
.to_string_lossy()
|
||||
.into_owned();
|
||||
TestConfig::SingleFile(SingleFile {
|
||||
name,
|
||||
file_path: test_file.into_path(),
|
||||
})
|
||||
})
|
||||
.collect();
|
||||
test_configs.into_iter()
|
||||
}
|
||||
|
||||
static TEST_PERMITS: Semaphore = Semaphore::const_new(8);
|
||||
|
||||
#[derive(Debug)]
|
||||
enum TestConfig {
|
||||
TestLayer(TestLayer),
|
||||
SingleFile(SingleFile),
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct TestLayer {
|
||||
name: String,
|
||||
children: Vec<TestConfig>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct SingleFile {
|
||||
name: String,
|
||||
file_path: PathBuf,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
enum TestResult {
|
||||
ResultLayer(ResultLayer),
|
||||
SingleFileResult(SingleFileResult),
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct ResultLayer {
|
||||
name: String,
|
||||
children: Vec<TestResult>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct SingleFileResult {
|
||||
name: String,
|
||||
file_path: PathBuf,
|
||||
status: TestStatus,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub(crate) enum TestStatus {
|
||||
Pass,
|
||||
Fail,
|
||||
}
|
||||
|
||||
impl TestConfig {
|
||||
fn run_test(self) -> BoxFuture<'static, Result<TestResult, JoinError>> {
|
||||
async move {
|
||||
match self {
|
||||
TestConfig::TestLayer(test) => Ok(TestResult::ResultLayer(test.run_test().await?)),
|
||||
TestConfig::SingleFile(test) => {
|
||||
Ok(TestResult::SingleFileResult(test.run_test().await?))
|
||||
}
|
||||
}
|
||||
}
|
||||
.boxed()
|
||||
}
|
||||
}
|
||||
|
||||
impl SingleFile {
|
||||
async fn run_test(self) -> Result<SingleFileResult, JoinError> {
|
||||
let _permit = TEST_PERMITS.acquire().await.unwrap();
|
||||
let result = silent_compare_on_file(&self.file_path).await;
|
||||
Ok(SingleFileResult {
|
||||
name: self.name,
|
||||
file_path: self.file_path,
|
||||
status: if let Ok(true) = result {
|
||||
TestStatus::Pass
|
||||
} else {
|
||||
TestStatus::Fail
|
||||
},
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl TestLayer {
|
||||
async fn run_test(self) -> Result<ResultLayer, JoinError> {
|
||||
let running_children: Vec<_> = self
|
||||
.children
|
||||
.into_iter()
|
||||
.map(|c| tokio::spawn(c.run_test()))
|
||||
.collect();
|
||||
let mut children = Vec::with_capacity(running_children.len());
|
||||
for c in running_children {
|
||||
children.push(c.await??);
|
||||
}
|
||||
Ok(ResultLayer {
|
||||
name: self.name,
|
||||
children,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl TestResult {
|
||||
pub fn print(&self) {
|
||||
self.print_indented(0);
|
||||
}
|
||||
|
||||
fn print_indented(&self, indentation: usize) {
|
||||
match self {
|
||||
TestResult::ResultLayer(result) => result.print_indented(indentation),
|
||||
TestResult::SingleFileResult(result) => result.print_indented(indentation),
|
||||
}
|
||||
}
|
||||
|
||||
fn has_bad_children(&self) -> bool {
|
||||
match self {
|
||||
TestResult::ResultLayer(result) => result.has_bad_children(),
|
||||
TestResult::SingleFileResult(result) => result.has_bad_children(),
|
||||
}
|
||||
}
|
||||
|
||||
fn is_immediately_bad(&self) -> bool {
|
||||
match self {
|
||||
TestResult::ResultLayer(result) => result.is_immediately_bad(),
|
||||
TestResult::SingleFileResult(result) => result.is_immediately_bad(),
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn foreground_color(red: u8, green: u8, blue: u8) -> String {
|
||||
if TestResult::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 TestResult::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 TestResult::should_use_color() {
|
||||
"\x1b[0m"
|
||||
} else {
|
||||
""
|
||||
}
|
||||
}
|
||||
|
||||
fn should_use_color() -> bool {
|
||||
!std::env::var("NO_COLOR").is_ok_and(|val| !val.is_empty())
|
||||
}
|
||||
}
|
||||
|
||||
impl SingleFileResult {
|
||||
fn print_indented(&self, indentation: usize) {
|
||||
match self.status {
|
||||
TestStatus::Pass => {
|
||||
println!(
|
||||
"{indentation}{color}PASS{reset} {name}",
|
||||
indentation = " ".repeat(indentation),
|
||||
color = TestResult::foreground_color(0, 255, 0),
|
||||
reset = TestResult::reset_color(),
|
||||
name = self.name
|
||||
);
|
||||
}
|
||||
TestStatus::Fail => {
|
||||
println!(
|
||||
"{indentation}{color}FAIL{reset} {name}",
|
||||
indentation = " ".repeat(indentation),
|
||||
color = TestResult::foreground_color(255, 0, 0),
|
||||
reset = TestResult::reset_color(),
|
||||
name = self.name
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn has_bad_children(&self) -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
fn is_immediately_bad(&self) -> bool {
|
||||
match self.status {
|
||||
TestStatus::Pass => false,
|
||||
TestStatus::Fail => true,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ResultLayer {
|
||||
fn print_indented(&self, indentation: usize) {
|
||||
if self.is_immediately_bad() {
|
||||
println!(
|
||||
"{indentation}{color}FAIL{reset} {name}",
|
||||
indentation = " ".repeat(indentation),
|
||||
color = TestResult::foreground_color(255, 0, 0),
|
||||
reset = TestResult::reset_color(),
|
||||
name = self.name
|
||||
);
|
||||
} else if self.has_bad_children() {
|
||||
println!(
|
||||
"{indentation}{color}BADCHILD{reset} {name}",
|
||||
indentation = " ".repeat(indentation),
|
||||
color = TestResult::foreground_color(255, 255, 0),
|
||||
reset = TestResult::reset_color(),
|
||||
name = self.name
|
||||
);
|
||||
} else {
|
||||
println!(
|
||||
"{indentation}{color}PASS{reset} {name}",
|
||||
indentation = " ".repeat(indentation),
|
||||
color = TestResult::foreground_color(0, 255, 0),
|
||||
reset = TestResult::reset_color(),
|
||||
name = self.name
|
||||
);
|
||||
}
|
||||
self.children
|
||||
.iter()
|
||||
.for_each(|result| result.print_indented(indentation + 1));
|
||||
}
|
||||
|
||||
fn has_bad_children(&self) -> bool {
|
||||
self.children
|
||||
.iter()
|
||||
.any(|result| result.is_immediately_bad() || result.has_bad_children())
|
||||
}
|
||||
|
||||
fn is_immediately_bad(&self) -> bool {
|
||||
false
|
||||
}
|
||||
}
|
||||
@@ -12,39 +12,60 @@ use crate::context::LocalFileAccessInterface;
|
||||
use crate::parser::parse_file_with_settings;
|
||||
use crate::parser::parse_with_settings;
|
||||
|
||||
pub fn run_anonymous_compare<P: AsRef<str>>(
|
||||
pub async fn run_anonymous_compare<P: AsRef<str>>(
|
||||
org_contents: P,
|
||||
) -> Result<(), Box<dyn std::error::Error>> {
|
||||
run_anonymous_compare_with_settings(org_contents, &GlobalSettings::default())
|
||||
) -> Result<bool, Box<dyn std::error::Error>> {
|
||||
run_anonymous_compare_with_settings(org_contents, &GlobalSettings::default(), false).await
|
||||
}
|
||||
|
||||
pub fn run_compare_on_file<P: AsRef<Path>>(org_path: P) -> Result<(), Box<dyn std::error::Error>> {
|
||||
run_compare_on_file_with_settings(org_path, &GlobalSettings::default())
|
||||
pub async fn run_compare_on_file<P: AsRef<Path>>(
|
||||
org_path: P,
|
||||
) -> Result<bool, Box<dyn std::error::Error>> {
|
||||
run_compare_on_file_with_settings(org_path, &GlobalSettings::default(), false).await
|
||||
}
|
||||
|
||||
pub fn run_anonymous_compare_with_settings<P: AsRef<str>>(
|
||||
pub async fn silent_anonymous_compare<P: AsRef<str>>(
|
||||
org_contents: P,
|
||||
global_settings: &GlobalSettings,
|
||||
) -> Result<(), Box<dyn std::error::Error>> {
|
||||
) -> Result<bool, Box<dyn std::error::Error>> {
|
||||
run_anonymous_compare_with_settings(org_contents, &GlobalSettings::default(), true).await
|
||||
}
|
||||
|
||||
pub async fn silent_compare_on_file<P: AsRef<Path>>(
|
||||
org_path: P,
|
||||
) -> Result<bool, Box<dyn std::error::Error>> {
|
||||
run_compare_on_file_with_settings(org_path, &GlobalSettings::default(), true).await
|
||||
}
|
||||
|
||||
pub async fn run_anonymous_compare_with_settings<'g, 's, P: AsRef<str>>(
|
||||
org_contents: P,
|
||||
global_settings: &GlobalSettings<'g, 's>,
|
||||
silent: bool,
|
||||
) -> Result<bool, Box<dyn std::error::Error>> {
|
||||
// TODO: This is a work-around to pretend that dos line endings do not exist. It would be better to handle the difference in line endings.
|
||||
let org_contents = org_contents.as_ref().replace("\r\n", "\n");
|
||||
let org_contents = org_contents.as_str();
|
||||
print_versions()?;
|
||||
if !silent {
|
||||
print_versions().await?;
|
||||
}
|
||||
let rust_parsed = parse_with_settings(org_contents, global_settings)?;
|
||||
let org_sexp = emacs_parse_anonymous_org_document(org_contents, global_settings)?;
|
||||
let org_sexp = emacs_parse_anonymous_org_document(org_contents, global_settings).await?;
|
||||
let (_remaining, parsed_sexp) = sexp(org_sexp.as_str()).map_err(|e| e.to_string())?;
|
||||
|
||||
println!("{}\n\n\n", org_contents);
|
||||
println!("{}", org_sexp);
|
||||
println!("{:#?}", rust_parsed);
|
||||
if !silent {
|
||||
println!("{}\n\n\n", org_contents);
|
||||
println!("{}", org_sexp);
|
||||
println!("{:#?}", rust_parsed);
|
||||
}
|
||||
|
||||
// We do the diffing after printing out both parsed forms in case the diffing panics
|
||||
let diff_result = compare_document(&parsed_sexp, &rust_parsed)?;
|
||||
diff_result.print(org_contents)?;
|
||||
if !silent {
|
||||
diff_result.print(org_contents)?;
|
||||
}
|
||||
|
||||
if diff_result.is_bad() {
|
||||
Err("Diff results do not match.")?;
|
||||
} else {
|
||||
return Ok(false);
|
||||
} else if !silent {
|
||||
println!(
|
||||
"{color}Entire document passes.{reset}",
|
||||
color = DiffResult::foreground_color(0, 255, 0),
|
||||
@@ -52,15 +73,18 @@ pub fn run_anonymous_compare_with_settings<P: AsRef<str>>(
|
||||
);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
Ok(true)
|
||||
}
|
||||
|
||||
pub fn run_compare_on_file_with_settings<P: AsRef<Path>>(
|
||||
pub async fn run_compare_on_file_with_settings<'g, 's, P: AsRef<Path>>(
|
||||
org_path: P,
|
||||
global_settings: &GlobalSettings,
|
||||
) -> Result<(), Box<dyn std::error::Error>> {
|
||||
global_settings: &GlobalSettings<'g, 's>,
|
||||
silent: bool,
|
||||
) -> Result<bool, Box<dyn std::error::Error>> {
|
||||
let org_path = org_path.as_ref();
|
||||
print_versions()?;
|
||||
if !silent {
|
||||
print_versions().await?;
|
||||
}
|
||||
let parent_directory = org_path
|
||||
.parent()
|
||||
.ok_or("Should be contained inside a directory.")?;
|
||||
@@ -77,20 +101,24 @@ pub fn run_compare_on_file_with_settings<P: AsRef<Path>>(
|
||||
global_settings
|
||||
};
|
||||
let rust_parsed = parse_file_with_settings(org_contents, &global_settings, Some(org_path))?;
|
||||
let org_sexp = emacs_parse_file_org_document(org_path, &global_settings)?;
|
||||
let org_sexp = emacs_parse_file_org_document(org_path, &global_settings).await?;
|
||||
let (_remaining, parsed_sexp) = sexp(org_sexp.as_str()).map_err(|e| e.to_string())?;
|
||||
|
||||
println!("{}\n\n\n", org_contents);
|
||||
println!("{}", org_sexp);
|
||||
println!("{:#?}", rust_parsed);
|
||||
if !silent {
|
||||
println!("{}\n\n\n", org_contents);
|
||||
println!("{}", org_sexp);
|
||||
println!("{:#?}", rust_parsed);
|
||||
}
|
||||
|
||||
// We do the diffing after printing out both parsed forms in case the diffing panics
|
||||
let diff_result = compare_document(&parsed_sexp, &rust_parsed)?;
|
||||
diff_result.print(org_contents)?;
|
||||
if !silent {
|
||||
diff_result.print(org_contents)?;
|
||||
}
|
||||
|
||||
if diff_result.is_bad() {
|
||||
Err("Diff results do not match.")?;
|
||||
} else {
|
||||
return Ok(false);
|
||||
} else if !silent {
|
||||
println!(
|
||||
"{color}Entire document passes.{reset}",
|
||||
color = DiffResult::foreground_color(0, 255, 0),
|
||||
@@ -98,11 +126,14 @@ pub fn run_compare_on_file_with_settings<P: AsRef<Path>>(
|
||||
);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
Ok(true)
|
||||
}
|
||||
|
||||
fn print_versions() -> Result<(), Box<dyn std::error::Error>> {
|
||||
eprintln!("Using emacs version: {}", get_emacs_version()?.trim());
|
||||
eprintln!("Using org-mode version: {}", get_org_mode_version()?.trim());
|
||||
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(())
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ use std::fmt::Debug;
|
||||
use std::str::FromStr;
|
||||
|
||||
use super::diff::artificial_diff_scope;
|
||||
use super::diff::artificial_owned_diff_scope;
|
||||
use super::diff::compare_ast_node;
|
||||
use super::diff::DiffEntry;
|
||||
use super::diff::DiffStatus;
|
||||
@@ -15,6 +16,7 @@ use super::util::get_property_unquoted_atom;
|
||||
use crate::types::AstNode;
|
||||
use crate::types::CharOffsetInLine;
|
||||
use crate::types::LineNumber;
|
||||
use crate::types::Object;
|
||||
use crate::types::RetainLabels;
|
||||
use crate::types::SwitchNumberLines;
|
||||
|
||||
@@ -32,6 +34,26 @@ pub(crate) enum ComparePropertiesResult<'b, 's> {
|
||||
DiffEntry(DiffEntry<'b, 's>),
|
||||
}
|
||||
|
||||
impl<'b, 's> ComparePropertiesResult<'b, 's> {
|
||||
pub(crate) fn apply(
|
||||
self,
|
||||
child_status: &mut Vec<DiffEntry<'b, 's>>,
|
||||
this_status: &mut DiffStatus,
|
||||
message: &mut Option<String>,
|
||||
) {
|
||||
match self {
|
||||
ComparePropertiesResult::NoChange => {}
|
||||
ComparePropertiesResult::SelfChange(new_status, new_message) => {
|
||||
*this_status = new_status;
|
||||
*message = new_message
|
||||
}
|
||||
ComparePropertiesResult::DiffEntry(diff_entry) => child_status.push(diff_entry),
|
||||
}
|
||||
|
||||
// foo
|
||||
}
|
||||
}
|
||||
|
||||
/// Do no comparison.
|
||||
///
|
||||
/// This is for when you want to acknowledge that a field exists in the emacs token, but you do not have any validation for it when using the compare_properties!() macro. Ideally, this should be kept to a minimum since this represents untested values.
|
||||
@@ -353,6 +375,15 @@ where
|
||||
let rust_value: Option<Vec<RV>> = rust_value.map(|it| it.collect());
|
||||
match (value, rust_value) {
|
||||
(None, None) => {}
|
||||
(Some(el), None)
|
||||
if el.len() == 1
|
||||
&& el.into_iter().all(|t| {
|
||||
if let Ok(r#""""#) = t.as_atom() {
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}) => {}
|
||||
(None, rv @ Some(_)) | (Some(_), rv @ None) => {
|
||||
let this_status = DiffStatus::Bad;
|
||||
let message = Some(format!(
|
||||
@@ -381,6 +412,126 @@ where
|
||||
Ok(ComparePropertiesResult::NoChange)
|
||||
}
|
||||
|
||||
/// Special compare used for affiliate keywords that are parsed as objects.
|
||||
///
|
||||
/// Org-mode seems to store these as a 3-deep list:
|
||||
/// - Outer list with 1 element per #+caption keyword (or other parsed keyword).
|
||||
/// - Middle list which has:
|
||||
/// - first element is a list of objects representing the value after the colon.
|
||||
/// - every additional element is a list of objects from inside the square brackets (the optional value).
|
||||
pub(crate) fn compare_property_list_of_list_of_list_of_ast_nodes<
|
||||
'b,
|
||||
's,
|
||||
'x,
|
||||
R,
|
||||
RG: Fn(R) -> Option<&'b Vec<(Option<Vec<Object<'s>>>, Vec<Object<'s>>)>>,
|
||||
>(
|
||||
source: &'s str,
|
||||
emacs: &'b Token<'s>,
|
||||
rust_node: R,
|
||||
emacs_field: &'x str,
|
||||
rust_value_getter: RG,
|
||||
) -> Result<ComparePropertiesResult<'b, 's>, Box<dyn std::error::Error>> {
|
||||
// TODO: Replace Object<'s> with generics. I hard-coded Object in to make lifetimes easier.
|
||||
let rust_value = rust_value_getter(rust_node);
|
||||
let value = get_property(emacs, emacs_field)?
|
||||
.map(Token::as_list)
|
||||
.map_or(Ok(None), |r| r.map(Some))?;
|
||||
let (value, rust_value) = match (value, rust_value) {
|
||||
(None, None) => {
|
||||
return Ok(ComparePropertiesResult::NoChange);
|
||||
}
|
||||
(None, Some(_)) | (Some(_), None) => {
|
||||
let this_status = DiffStatus::Bad;
|
||||
let message = Some(format!(
|
||||
"{} mismatch (emacs != rust) {:?} != {:?}",
|
||||
emacs_field, value, rust_value
|
||||
));
|
||||
return Ok(ComparePropertiesResult::SelfChange(this_status, message));
|
||||
}
|
||||
(Some(value), Some(rust_value)) if value.len() != rust_value.len() => {
|
||||
let this_status = DiffStatus::Bad;
|
||||
let message = Some(format!(
|
||||
"{} mismatch (emacs != rust) {:?} != {:?}",
|
||||
emacs_field, value, rust_value
|
||||
));
|
||||
return Ok(ComparePropertiesResult::SelfChange(this_status, message));
|
||||
}
|
||||
(Some(value), Some(rust_value)) => (value, rust_value),
|
||||
};
|
||||
|
||||
let mut full_status: Vec<DiffEntry<'b, 's>> = Vec::with_capacity(rust_value.len());
|
||||
|
||||
// Iterate the outer lists
|
||||
for (value, (rust_optional, rust_value)) in value.iter().zip(rust_value.iter()) {
|
||||
let mut middle_value = value.as_list()?.iter();
|
||||
// First element of middle list is the mandatory value (the value past the colon).
|
||||
let mandatory_value = middle_value.next();
|
||||
let mandatory_value = match mandatory_value {
|
||||
Some(mandatory_value) => mandatory_value,
|
||||
None => {
|
||||
let this_status = DiffStatus::Bad;
|
||||
let message = Some(format!(
|
||||
"{} mismatch (emacs != rust) {:?} != {:?}",
|
||||
emacs_field, value, rust_value
|
||||
));
|
||||
return Ok(ComparePropertiesResult::SelfChange(this_status, message));
|
||||
}
|
||||
};
|
||||
|
||||
// Compare optional value
|
||||
if let Some(rust_optional) = rust_optional {
|
||||
let mut child_status: Vec<DiffEntry<'b, 's>> = Vec::with_capacity(rust_value.len());
|
||||
if rust_optional.len() != middle_value.len() {
|
||||
let this_status = DiffStatus::Bad;
|
||||
let message = Some(format!(
|
||||
"{} optional value length mismatch (emacs != rust) {} != {} | {:?}",
|
||||
emacs_field,
|
||||
middle_value.len(),
|
||||
rust_optional.len(),
|
||||
rust_optional
|
||||
));
|
||||
return Ok(ComparePropertiesResult::SelfChange(this_status, message));
|
||||
}
|
||||
for (e, r) in middle_value.zip(rust_optional) {
|
||||
child_status.push(compare_ast_node(source, e, r.into())?);
|
||||
}
|
||||
if !child_status.is_empty() {
|
||||
let diff_scope = artificial_diff_scope("optional value", child_status)?;
|
||||
full_status.push(diff_scope);
|
||||
}
|
||||
}
|
||||
|
||||
// Compare mandatory value
|
||||
let mut child_status: Vec<DiffEntry<'b, 's>> = Vec::with_capacity(rust_value.len());
|
||||
let mandatory_value = mandatory_value.as_list()?;
|
||||
if rust_value.len() != mandatory_value.len() {
|
||||
let this_status = DiffStatus::Bad;
|
||||
let message = Some(format!(
|
||||
"{} mandatory value length mismatch (emacs != rust) {} != {} | {:?}",
|
||||
emacs_field,
|
||||
mandatory_value.len(),
|
||||
rust_value.len(),
|
||||
rust_value
|
||||
));
|
||||
return Ok(ComparePropertiesResult::SelfChange(this_status, message));
|
||||
}
|
||||
for (e, r) in mandatory_value.iter().zip(rust_value) {
|
||||
child_status.push(compare_ast_node(source, e, r.into())?);
|
||||
}
|
||||
if !child_status.is_empty() {
|
||||
let diff_scope = artificial_diff_scope("mandatory value", child_status)?;
|
||||
full_status.push(diff_scope);
|
||||
}
|
||||
}
|
||||
if full_status.is_empty() {
|
||||
Ok(ComparePropertiesResult::NoChange)
|
||||
} else {
|
||||
let diff_scope = artificial_owned_diff_scope(emacs_field, full_status)?;
|
||||
Ok(ComparePropertiesResult::DiffEntry(diff_scope))
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn compare_property_number_lines<
|
||||
'b,
|
||||
's,
|
||||
|
||||
@@ -2,7 +2,6 @@ use std::borrow::Cow;
|
||||
// TODO: Update all to use the macro to assert there are no unexpected keys.
|
||||
// TODO: Check all compare funtions for whether they correctly iterate children.
|
||||
use std::collections::BTreeSet;
|
||||
use std::collections::HashSet;
|
||||
|
||||
use super::compare_field::compare_identity;
|
||||
use super::compare_field::compare_noop;
|
||||
@@ -21,13 +20,13 @@ 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::assert_no_children;
|
||||
use super::util::compare_additional_properties;
|
||||
use super::util::compare_affiliated_keywords;
|
||||
use super::util::compare_children;
|
||||
use super::util::compare_children_iter;
|
||||
use super::util::compare_standard_properties;
|
||||
use super::util::get_property;
|
||||
use super::util::get_property_boolean;
|
||||
use super::util::get_property_quoted_string;
|
||||
use crate::compare::compare_field::ComparePropertiesResult;
|
||||
use crate::compare::compare_field::EmacsField;
|
||||
use crate::compare::macros::compare_properties;
|
||||
@@ -48,7 +47,6 @@ use crate::types::Date;
|
||||
use crate::types::DayOfMonth;
|
||||
use crate::types::DiarySexp;
|
||||
use crate::types::Document;
|
||||
use crate::types::DocumentElement;
|
||||
use crate::types::Drawer;
|
||||
use crate::types::DynamicBlock;
|
||||
use crate::types::Entity;
|
||||
@@ -82,7 +80,6 @@ use crate::types::PlainListItem;
|
||||
use crate::types::PlainListType;
|
||||
use crate::types::PlainText;
|
||||
use crate::types::Planning;
|
||||
use crate::types::PriorityCookie;
|
||||
use crate::types::PropertyDrawer;
|
||||
use crate::types::QuoteBlock;
|
||||
use crate::types::RadioLink;
|
||||
@@ -333,6 +330,17 @@ pub(crate) fn artificial_diff_scope<'b, 's>(
|
||||
.into())
|
||||
}
|
||||
|
||||
pub(crate) fn artificial_owned_diff_scope<'b, 's, 'x>(
|
||||
name: &'x str,
|
||||
children: Vec<DiffEntry<'b, 's>>,
|
||||
) -> Result<DiffEntry<'b, 's>, Box<dyn std::error::Error>> {
|
||||
Ok(DiffLayer {
|
||||
name: name.to_string().into(),
|
||||
children,
|
||||
}
|
||||
.into())
|
||||
}
|
||||
|
||||
pub(crate) fn compare_ast_node<'b, 's>(
|
||||
source: &'s str,
|
||||
emacs: &'b Token<'s>,
|
||||
@@ -447,6 +455,27 @@ fn _compare_document<'b, 's>(
|
||||
let mut child_status = Vec::new();
|
||||
let mut message = None;
|
||||
|
||||
let additional_property_names: Vec<String> = rust
|
||||
.get_additional_properties()
|
||||
.map(|node_property| format!(":{}", node_property.property_name.to_uppercase()))
|
||||
.collect();
|
||||
|
||||
let additional_properties: Vec<(String, &str)> = rust
|
||||
.get_additional_properties()
|
||||
.map(|node_property| {
|
||||
(
|
||||
format!(":{}", node_property.property_name.to_uppercase()),
|
||||
node_property.value.unwrap_or(""),
|
||||
)
|
||||
})
|
||||
.collect();
|
||||
|
||||
compare_additional_properties(emacs, additional_properties.into_iter())?.apply(
|
||||
&mut child_status,
|
||||
&mut this_status,
|
||||
&mut message,
|
||||
);
|
||||
|
||||
compare_children_iter(
|
||||
source,
|
||||
emacs,
|
||||
@@ -460,6 +489,10 @@ fn _compare_document<'b, 's>(
|
||||
source,
|
||||
emacs,
|
||||
rust,
|
||||
additional_property_names
|
||||
.iter()
|
||||
.map(String::as_str)
|
||||
.map(EmacsField::Required),
|
||||
(
|
||||
EmacsField::Required(":path"),
|
||||
|r| r.path.as_ref().map(|p| p.to_str()).flatten(),
|
||||
@@ -533,7 +566,7 @@ fn compare_section<'b, 's>(
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
fn new_compare_heading<'b, 's>(
|
||||
fn compare_heading<'b, 's>(
|
||||
source: &'s str,
|
||||
emacs: &'b Token<'s>,
|
||||
rust: &'b Heading<'s>,
|
||||
@@ -542,7 +575,26 @@ fn new_compare_heading<'b, 's>(
|
||||
let mut child_status = Vec::new();
|
||||
let mut message = None;
|
||||
|
||||
// TODO: This needs to support additional properties from the property drawer
|
||||
let additional_property_names: Vec<String> = rust
|
||||
.get_additional_properties()
|
||||
.map(|node_property| format!(":{}", node_property.property_name.to_uppercase()))
|
||||
.collect();
|
||||
|
||||
let additional_properties: Vec<(String, &str)> = rust
|
||||
.get_additional_properties()
|
||||
.map(|node_property| {
|
||||
(
|
||||
format!(":{}", node_property.property_name.to_uppercase()),
|
||||
node_property.value.unwrap_or(""),
|
||||
)
|
||||
})
|
||||
.collect();
|
||||
|
||||
compare_additional_properties(emacs, additional_properties.into_iter())?.apply(
|
||||
&mut child_status,
|
||||
&mut this_status,
|
||||
&mut message,
|
||||
);
|
||||
|
||||
compare_children(
|
||||
source,
|
||||
@@ -557,6 +609,10 @@ fn new_compare_heading<'b, 's>(
|
||||
source,
|
||||
emacs,
|
||||
rust,
|
||||
additional_property_names
|
||||
.iter()
|
||||
.map(String::as_str)
|
||||
.map(EmacsField::Required),
|
||||
(
|
||||
EmacsField::Required(":level"),
|
||||
|r| Some(r.level),
|
||||
@@ -664,278 +720,6 @@ fn new_compare_heading<'b, 's>(
|
||||
.into())
|
||||
}
|
||||
|
||||
fn compare_heading<'b, 's>(
|
||||
source: &'s str,
|
||||
emacs: &'b Token<'s>,
|
||||
rust: &'b Heading<'s>,
|
||||
) -> Result<DiffEntry<'b, 's>, Box<dyn std::error::Error>> {
|
||||
let children = emacs.as_list()?;
|
||||
let mut child_status = Vec::new();
|
||||
let mut this_status = DiffStatus::Good;
|
||||
let mut message = None;
|
||||
|
||||
// Compare level
|
||||
let level = get_property(emacs, ":level")?
|
||||
.ok_or("Level should not be nil")?
|
||||
.as_atom()?;
|
||||
if rust.level.to_string() != level {
|
||||
this_status = DiffStatus::Bad;
|
||||
message = Some(format!(
|
||||
"Headline level do not match (emacs != rust): {} != {}",
|
||||
level, rust.level
|
||||
))
|
||||
}
|
||||
|
||||
// Compare tags
|
||||
let emacs_tags = get_tags_from_heading(emacs)?;
|
||||
let emacs_tags: HashSet<_> = emacs_tags.iter().map(|val| val.as_str()).collect();
|
||||
let rust_tags: HashSet<&str> = rust.tags.iter().map(|val| *val).collect();
|
||||
let difference: Vec<&str> = emacs_tags
|
||||
.symmetric_difference(&rust_tags)
|
||||
.map(|val| *val)
|
||||
.collect();
|
||||
if !difference.is_empty() {
|
||||
this_status = DiffStatus::Bad;
|
||||
message = Some(format!("Mismatched tags: {}", difference.join(", ")));
|
||||
}
|
||||
|
||||
// Compare todo-keyword
|
||||
let todo_keyword = get_property(emacs, ":todo-keyword")?
|
||||
.map(Token::as_atom)
|
||||
.map_or(Ok(None), |r| r.map(Some))?
|
||||
.unwrap_or("nil");
|
||||
match (todo_keyword, &rust.todo_keyword, unquote(todo_keyword)) {
|
||||
("nil", None, _) => {}
|
||||
(_, Some((_rust_todo_type, rust_todo)), Ok(emacs_todo)) if emacs_todo == *rust_todo => {}
|
||||
(emacs_todo, rust_todo, _) => {
|
||||
this_status = DiffStatus::Bad;
|
||||
message = Some(format!(
|
||||
"(emacs != rust) {:?} != {:?}",
|
||||
emacs_todo, rust_todo
|
||||
));
|
||||
}
|
||||
};
|
||||
// Compare todo-type
|
||||
let todo_type = get_property(emacs, ":todo-type")?
|
||||
.map(Token::as_atom)
|
||||
.map_or(Ok(None), |r| r.map(Some))?
|
||||
.unwrap_or("nil");
|
||||
// todo-type is an unquoted string either todo, done, or nil
|
||||
match (todo_type, &rust.todo_keyword) {
|
||||
("nil", None) => {}
|
||||
("todo", Some((TodoKeywordType::Todo, _))) => {}
|
||||
("done", Some((TodoKeywordType::Done, _))) => {}
|
||||
(emacs_todo, rust_todo) => {
|
||||
this_status = DiffStatus::Bad;
|
||||
message = Some(format!(
|
||||
"(emacs != rust) {:?} != {:?}",
|
||||
emacs_todo, rust_todo
|
||||
));
|
||||
}
|
||||
};
|
||||
|
||||
// Compare title
|
||||
let title = get_property(emacs, ":title")?;
|
||||
match (title, rust.title.len()) {
|
||||
(None, 0) => {}
|
||||
(None, _) => {
|
||||
this_status = DiffStatus::Bad;
|
||||
message = Some(format!(
|
||||
"Titles do not match (emacs != rust): {:?} != {:?}",
|
||||
title, rust.title
|
||||
))
|
||||
}
|
||||
(Some(title), _) => {
|
||||
let title_status = title
|
||||
.as_list()?
|
||||
.iter()
|
||||
.zip(rust.title.iter())
|
||||
.map(|(emacs_child, rust_child)| {
|
||||
compare_ast_node(source, emacs_child, rust_child.into())
|
||||
})
|
||||
.collect::<Result<Vec<_>, _>>()?;
|
||||
child_status.push(artificial_diff_scope("title", title_status)?);
|
||||
}
|
||||
};
|
||||
|
||||
// Compare priority
|
||||
let priority = get_property(emacs, ":priority")?;
|
||||
match (priority, rust.priority_cookie) {
|
||||
(None, None) => {}
|
||||
(None, Some(_)) | (Some(_), None) => {
|
||||
this_status = DiffStatus::Bad;
|
||||
message = Some(format!(
|
||||
"Priority cookie mismatch (emacs != rust) {:?} != {:?}",
|
||||
priority, rust.priority_cookie
|
||||
));
|
||||
}
|
||||
(Some(emacs_priority_cookie), Some(rust_priority_cookie)) => {
|
||||
let emacs_priority_cookie =
|
||||
emacs_priority_cookie.as_atom()?.parse::<PriorityCookie>()?;
|
||||
if emacs_priority_cookie != rust_priority_cookie {
|
||||
this_status = DiffStatus::Bad;
|
||||
message = Some(format!(
|
||||
"Priority cookie mismatch (emacs != rust) {:?} != {:?}",
|
||||
emacs_priority_cookie, rust_priority_cookie
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Compare archived
|
||||
let archived = get_property(emacs, ":archivedp")?;
|
||||
match (archived, rust.is_archived) {
|
||||
(None, true) | (Some(_), false) => {
|
||||
this_status = DiffStatus::Bad;
|
||||
message = Some(format!(
|
||||
"archived mismatch (emacs != rust) {:?} != {:?}",
|
||||
archived, rust.is_archived
|
||||
));
|
||||
}
|
||||
(None, false) | (Some(_), true) => {}
|
||||
}
|
||||
|
||||
// Compare commented
|
||||
let commented = get_property(emacs, ":commentedp")?;
|
||||
match (commented, rust.is_comment) {
|
||||
(None, true) | (Some(_), false) => {
|
||||
this_status = DiffStatus::Bad;
|
||||
message = Some(format!(
|
||||
"commented mismatch (emacs != rust) {:?} != {:?}",
|
||||
commented, rust.is_comment
|
||||
));
|
||||
}
|
||||
(None, false) | (Some(_), true) => {}
|
||||
}
|
||||
|
||||
// Compare raw-value
|
||||
let raw_value = get_property_quoted_string(emacs, ":raw-value")?
|
||||
.ok_or("Headlines should have :raw-value.")?;
|
||||
let rust_raw_value = rust.get_raw_value();
|
||||
if raw_value != rust_raw_value {
|
||||
this_status = DiffStatus::Bad;
|
||||
message = Some(format!(
|
||||
"raw-value mismatch (emacs != rust) {:?} != {:?}",
|
||||
raw_value, rust_raw_value
|
||||
));
|
||||
}
|
||||
|
||||
// Compare footnote-section-p
|
||||
let footnote_section = get_property_boolean(emacs, ":footnote-section-p")?;
|
||||
if footnote_section != rust.is_footnote_section {
|
||||
this_status = DiffStatus::Bad;
|
||||
message = Some(format!(
|
||||
"footnote section mismatch (emacs != rust) {:?} != {:?}",
|
||||
footnote_section, rust.is_footnote_section
|
||||
));
|
||||
}
|
||||
|
||||
// Compare scheduled
|
||||
let scheduled = get_property(emacs, ":scheduled")?;
|
||||
match (scheduled, &rust.scheduled) {
|
||||
(None, None) => {}
|
||||
(None, Some(_)) | (Some(_), None) => {
|
||||
this_status = DiffStatus::Bad;
|
||||
message = Some(format!(
|
||||
"Scheduled mismatch (emacs != rust) {:?} != {:?}",
|
||||
scheduled, rust.scheduled
|
||||
));
|
||||
}
|
||||
(Some(emacs_child), Some(rust_child)) => {
|
||||
let result = compare_ast_node(source, emacs_child, rust_child.into())?;
|
||||
child_status.push(artificial_diff_scope("scheduled", vec![result])?);
|
||||
}
|
||||
}
|
||||
|
||||
// Compare deadline
|
||||
let deadline = get_property(emacs, ":deadline")?;
|
||||
match (deadline, &rust.deadline) {
|
||||
(None, None) => {}
|
||||
(None, Some(_)) | (Some(_), None) => {
|
||||
this_status = DiffStatus::Bad;
|
||||
message = Some(format!(
|
||||
"Deadline mismatch (emacs != rust) {:?} != {:?}",
|
||||
deadline, rust.deadline
|
||||
));
|
||||
}
|
||||
(Some(emacs_child), Some(rust_child)) => {
|
||||
let result = compare_ast_node(source, emacs_child, rust_child.into())?;
|
||||
child_status.push(artificial_diff_scope("deadline", vec![result])?);
|
||||
}
|
||||
}
|
||||
|
||||
// Compare closed
|
||||
let closed = get_property(emacs, ":closed")?;
|
||||
match (closed, &rust.closed) {
|
||||
(None, None) => {}
|
||||
(None, Some(_)) | (Some(_), None) => {
|
||||
this_status = DiffStatus::Bad;
|
||||
message = Some(format!(
|
||||
"Closed mismatch (emacs != rust) {:?} != {:?}",
|
||||
closed, rust.closed
|
||||
));
|
||||
}
|
||||
(Some(emacs_child), Some(rust_child)) => {
|
||||
let result = compare_ast_node(source, emacs_child, rust_child.into())?;
|
||||
child_status.push(artificial_diff_scope("closed", vec![result])?);
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Compare :pre-blank
|
||||
|
||||
// Compare section
|
||||
let section_status = children
|
||||
.iter()
|
||||
.skip(2)
|
||||
.zip(rust.children.iter())
|
||||
.map(|(emacs_child, rust_child)| match rust_child {
|
||||
DocumentElement::Heading(rust_heading) => {
|
||||
compare_ast_node(source, emacs_child, rust_heading.into())
|
||||
}
|
||||
DocumentElement::Section(rust_section) => {
|
||||
compare_ast_node(source, emacs_child, rust_section.into())
|
||||
}
|
||||
})
|
||||
.collect::<Result<Vec<_>, _>>()?;
|
||||
child_status.push(artificial_diff_scope("section", section_status)?);
|
||||
|
||||
Ok(DiffResult {
|
||||
status: this_status,
|
||||
name: rust.get_elisp_name(),
|
||||
message,
|
||||
children: child_status,
|
||||
rust_source: rust.get_source(),
|
||||
emacs_token: emacs,
|
||||
}
|
||||
.into())
|
||||
}
|
||||
|
||||
fn get_tags_from_heading<'b, 's>(
|
||||
emacs: &'b Token<'s>,
|
||||
) -> Result<HashSet<String>, Box<dyn std::error::Error>> {
|
||||
let tags = match get_property(emacs, ":tags")? {
|
||||
Some(prop) => prop,
|
||||
None => return Ok(HashSet::new()),
|
||||
};
|
||||
|
||||
match tags.as_atom() {
|
||||
Ok(val) => panic!("Unexpected value for tags: {:?}", val),
|
||||
Err(_) => {}
|
||||
};
|
||||
let tags = {
|
||||
let tags = tags.as_list()?;
|
||||
let strings = tags
|
||||
.iter()
|
||||
.map(Token::as_atom)
|
||||
.collect::<Result<Vec<&str>, _>>()?;
|
||||
strings
|
||||
.into_iter()
|
||||
.map(unquote)
|
||||
.collect::<Result<HashSet<String>, _>>()?
|
||||
};
|
||||
Ok(tags)
|
||||
}
|
||||
|
||||
fn compare_paragraph<'b, 's>(
|
||||
source: &'s str,
|
||||
emacs: &'b Token<'s>,
|
||||
@@ -954,21 +738,7 @@ fn compare_paragraph<'b, 's>(
|
||||
&mut message,
|
||||
)?;
|
||||
|
||||
for diff in compare_properties!(
|
||||
source,
|
||||
emacs,
|
||||
rust,
|
||||
(
|
||||
EmacsField::Optional(":name"),
|
||||
|r| r.name,
|
||||
compare_property_quoted_string
|
||||
),
|
||||
(
|
||||
EmacsField::Optional(":caption"),
|
||||
compare_identity,
|
||||
compare_noop
|
||||
)
|
||||
) {
|
||||
for diff in compare_properties!(source, emacs, rust, [],) {
|
||||
match diff {
|
||||
ComparePropertiesResult::NoChange => {}
|
||||
ComparePropertiesResult::SelfChange(new_status, new_message) => {
|
||||
@@ -1012,16 +782,7 @@ fn compare_plain_list<'b, 's>(
|
||||
source,
|
||||
emacs,
|
||||
rust,
|
||||
(
|
||||
EmacsField::Optional(":name"),
|
||||
|r| r.name,
|
||||
compare_property_quoted_string
|
||||
),
|
||||
(
|
||||
EmacsField::Optional(":caption"),
|
||||
compare_identity,
|
||||
compare_noop
|
||||
),
|
||||
[],
|
||||
(
|
||||
EmacsField::Required(":type"),
|
||||
|r| Some(match r.list_type {
|
||||
@@ -1139,24 +900,28 @@ fn compare_center_block<'b, 's>(
|
||||
emacs: &'b Token<'s>,
|
||||
rust: &'b CenterBlock<'s>,
|
||||
) -> Result<DiffEntry<'b, 's>, Box<dyn std::error::Error>> {
|
||||
let children = emacs.as_list()?;
|
||||
let mut child_status = Vec::new();
|
||||
let mut this_status = DiffStatus::Good;
|
||||
let mut child_status = Vec::new();
|
||||
let mut message = None;
|
||||
|
||||
// TODO: Compare :caption
|
||||
// Compare name
|
||||
let name = get_property_quoted_string(emacs, ":name")?;
|
||||
if name.as_ref().map(String::as_str) != rust.name {
|
||||
this_status = DiffStatus::Bad;
|
||||
message = Some(format!(
|
||||
"Name mismatch (emacs != rust) {:?} != {:?}",
|
||||
name, rust.name
|
||||
));
|
||||
}
|
||||
compare_children(
|
||||
source,
|
||||
emacs,
|
||||
&rust.children,
|
||||
&mut child_status,
|
||||
&mut this_status,
|
||||
&mut message,
|
||||
)?;
|
||||
|
||||
for (emacs_child, rust_child) in children.iter().skip(2).zip(rust.children.iter()) {
|
||||
child_status.push(compare_ast_node(source, emacs_child, rust_child.into())?);
|
||||
for diff in compare_properties!(source, emacs, rust, [],) {
|
||||
match diff {
|
||||
ComparePropertiesResult::NoChange => {}
|
||||
ComparePropertiesResult::SelfChange(new_status, new_message) => {
|
||||
this_status = new_status;
|
||||
message = new_message
|
||||
}
|
||||
ComparePropertiesResult::DiffEntry(diff_entry) => child_status.push(diff_entry),
|
||||
}
|
||||
}
|
||||
|
||||
Ok(DiffResult {
|
||||
@@ -1188,21 +953,7 @@ fn compare_quote_block<'b, 's>(
|
||||
&mut message,
|
||||
)?;
|
||||
|
||||
for diff in compare_properties!(
|
||||
source,
|
||||
emacs,
|
||||
rust,
|
||||
(
|
||||
EmacsField::Optional(":name"),
|
||||
|r| r.name,
|
||||
compare_property_quoted_string
|
||||
),
|
||||
(
|
||||
EmacsField::Optional(":caption"),
|
||||
compare_identity,
|
||||
compare_noop
|
||||
)
|
||||
) {
|
||||
for diff in compare_properties!(source, emacs, rust, [],) {
|
||||
match diff {
|
||||
ComparePropertiesResult::NoChange => {}
|
||||
ComparePropertiesResult::SelfChange(new_status, new_message) => {
|
||||
@@ -1246,16 +997,7 @@ fn compare_special_block<'b, 's>(
|
||||
source,
|
||||
emacs,
|
||||
rust,
|
||||
(
|
||||
EmacsField::Optional(":name"),
|
||||
|r| r.name,
|
||||
compare_property_quoted_string
|
||||
),
|
||||
(
|
||||
EmacsField::Optional(":caption"),
|
||||
compare_identity,
|
||||
compare_noop
|
||||
),
|
||||
[],
|
||||
(
|
||||
EmacsField::Required(":type"),
|
||||
|r| Some(r.block_type),
|
||||
@@ -1315,16 +1057,7 @@ fn compare_dynamic_block<'b, 's>(
|
||||
source,
|
||||
emacs,
|
||||
rust,
|
||||
(
|
||||
EmacsField::Optional(":name"),
|
||||
|r| r.name,
|
||||
compare_property_quoted_string
|
||||
),
|
||||
(
|
||||
EmacsField::Optional(":caption"),
|
||||
compare_identity,
|
||||
compare_noop
|
||||
),
|
||||
[],
|
||||
(
|
||||
EmacsField::Required(":block-name"),
|
||||
|r| Some(r.block_name),
|
||||
@@ -1379,16 +1112,7 @@ fn compare_footnote_definition<'b, 's>(
|
||||
source,
|
||||
emacs,
|
||||
rust,
|
||||
(
|
||||
EmacsField::Optional(":name"),
|
||||
|r| r.name,
|
||||
compare_property_quoted_string
|
||||
),
|
||||
(
|
||||
EmacsField::Optional(":caption"),
|
||||
compare_identity,
|
||||
compare_noop
|
||||
),
|
||||
[],
|
||||
(
|
||||
EmacsField::Required(":label"),
|
||||
|r| Some(r.label),
|
||||
@@ -1485,16 +1209,7 @@ fn compare_drawer<'b, 's>(
|
||||
source,
|
||||
emacs,
|
||||
rust,
|
||||
(
|
||||
EmacsField::Optional(":name"),
|
||||
|r| r.name,
|
||||
compare_property_quoted_string
|
||||
),
|
||||
(
|
||||
EmacsField::Optional(":caption"),
|
||||
compare_identity,
|
||||
compare_noop
|
||||
),
|
||||
[],
|
||||
(
|
||||
EmacsField::Required(":drawer-name"),
|
||||
|r| Some(r.drawer_name),
|
||||
@@ -1631,16 +1346,7 @@ fn compare_table<'b, 's>(
|
||||
source,
|
||||
emacs,
|
||||
rust,
|
||||
(
|
||||
EmacsField::Optional(":name"),
|
||||
|r| r.name,
|
||||
compare_property_quoted_string
|
||||
),
|
||||
(
|
||||
EmacsField::Optional(":caption"),
|
||||
compare_identity,
|
||||
compare_noop
|
||||
),
|
||||
[],
|
||||
(
|
||||
EmacsField::Required(":tblfm"),
|
||||
|r| if r.formulas.is_empty() {
|
||||
@@ -1792,21 +1498,7 @@ fn compare_verse_block<'b, 's>(
|
||||
&mut message,
|
||||
)?;
|
||||
|
||||
for diff in compare_properties!(
|
||||
source,
|
||||
emacs,
|
||||
rust,
|
||||
(
|
||||
EmacsField::Optional(":name"),
|
||||
|r| r.name,
|
||||
compare_property_quoted_string
|
||||
),
|
||||
(
|
||||
EmacsField::Optional(":caption"),
|
||||
compare_identity,
|
||||
compare_noop
|
||||
)
|
||||
) {
|
||||
for diff in compare_properties!(source, emacs, rust, [],) {
|
||||
match diff {
|
||||
ComparePropertiesResult::NoChange => {}
|
||||
ComparePropertiesResult::SelfChange(new_status, new_message) => {
|
||||
@@ -1843,16 +1535,7 @@ fn compare_comment_block<'b, 's>(
|
||||
source,
|
||||
emacs,
|
||||
rust,
|
||||
(
|
||||
EmacsField::Optional(":name"),
|
||||
|r| r.name,
|
||||
compare_property_quoted_string
|
||||
),
|
||||
(
|
||||
EmacsField::Optional(":caption"),
|
||||
compare_identity,
|
||||
compare_noop
|
||||
),
|
||||
[],
|
||||
(
|
||||
EmacsField::Required(":value"),
|
||||
|r| Some(r.contents),
|
||||
@@ -1895,16 +1578,7 @@ fn compare_example_block<'b, 's>(
|
||||
source,
|
||||
emacs,
|
||||
rust,
|
||||
(
|
||||
EmacsField::Optional(":name"),
|
||||
|r| r.name,
|
||||
compare_property_quoted_string
|
||||
),
|
||||
(
|
||||
EmacsField::Optional(":caption"),
|
||||
compare_identity,
|
||||
compare_noop
|
||||
),
|
||||
[],
|
||||
(
|
||||
EmacsField::Required(":value"),
|
||||
|r| Some(r.contents.as_str()),
|
||||
@@ -1977,16 +1651,7 @@ fn compare_export_block<'b, 's>(
|
||||
source,
|
||||
emacs,
|
||||
rust,
|
||||
(
|
||||
EmacsField::Optional(":name"),
|
||||
|r| r.name,
|
||||
compare_property_quoted_string
|
||||
),
|
||||
(
|
||||
EmacsField::Optional(":caption"),
|
||||
compare_identity,
|
||||
compare_noop
|
||||
),
|
||||
[],
|
||||
(
|
||||
EmacsField::Required(":type"),
|
||||
|r| r.get_export_type(),
|
||||
@@ -2034,16 +1699,7 @@ fn compare_src_block<'b, 's>(
|
||||
source,
|
||||
emacs,
|
||||
rust,
|
||||
(
|
||||
EmacsField::Optional(":name"),
|
||||
|r| r.name,
|
||||
compare_property_quoted_string
|
||||
),
|
||||
(
|
||||
EmacsField::Optional(":caption"),
|
||||
compare_identity,
|
||||
compare_noop
|
||||
),
|
||||
[],
|
||||
(
|
||||
EmacsField::Required(":language"),
|
||||
|r| r.language,
|
||||
@@ -2181,16 +1837,7 @@ fn compare_diary_sexp<'b, 's>(
|
||||
source,
|
||||
emacs,
|
||||
rust,
|
||||
(
|
||||
EmacsField::Optional(":name"),
|
||||
|r| r.name,
|
||||
compare_property_quoted_string
|
||||
),
|
||||
(
|
||||
EmacsField::Optional(":caption"),
|
||||
compare_identity,
|
||||
compare_noop
|
||||
),
|
||||
[],
|
||||
(
|
||||
EmacsField::Required(":value"),
|
||||
|r| Some(r.value),
|
||||
@@ -2285,16 +1932,7 @@ fn compare_fixed_width_area<'b, 's>(
|
||||
source,
|
||||
emacs,
|
||||
rust,
|
||||
(
|
||||
EmacsField::Optional(":name"),
|
||||
|r| r.name,
|
||||
compare_property_quoted_string
|
||||
),
|
||||
(
|
||||
EmacsField::Optional(":caption"),
|
||||
compare_identity,
|
||||
compare_noop
|
||||
),
|
||||
[],
|
||||
(
|
||||
EmacsField::Required(":value"),
|
||||
|r| Some(r.get_value()),
|
||||
@@ -2333,21 +1971,7 @@ fn compare_horizontal_rule<'b, 's>(
|
||||
|
||||
assert_no_children(emacs, &mut this_status, &mut message)?;
|
||||
|
||||
for diff in compare_properties!(
|
||||
source,
|
||||
emacs,
|
||||
rust,
|
||||
(
|
||||
EmacsField::Optional(":name"),
|
||||
|r| r.name,
|
||||
compare_property_quoted_string
|
||||
),
|
||||
(
|
||||
EmacsField::Optional(":caption"),
|
||||
compare_identity,
|
||||
compare_noop
|
||||
)
|
||||
) {
|
||||
for diff in compare_properties!(source, emacs, rust, [],) {
|
||||
match diff {
|
||||
ComparePropertiesResult::NoChange => {}
|
||||
ComparePropertiesResult::SelfChange(new_status, new_message) => {
|
||||
@@ -2384,16 +2008,7 @@ fn compare_keyword<'b, 's>(
|
||||
source,
|
||||
emacs,
|
||||
rust,
|
||||
(
|
||||
EmacsField::Optional(":name"),
|
||||
|r| r.name,
|
||||
compare_property_quoted_string
|
||||
),
|
||||
(
|
||||
EmacsField::Optional(":caption"),
|
||||
compare_identity,
|
||||
compare_noop
|
||||
),
|
||||
[],
|
||||
(
|
||||
EmacsField::Required(":key"),
|
||||
|r| Some(r.key.to_uppercase()),
|
||||
@@ -2441,16 +2056,7 @@ fn compare_babel_call<'b, 's>(
|
||||
source,
|
||||
emacs,
|
||||
rust,
|
||||
(
|
||||
EmacsField::Optional(":name"),
|
||||
|r| r.name,
|
||||
compare_property_quoted_string
|
||||
),
|
||||
(
|
||||
EmacsField::Optional(":caption"),
|
||||
compare_identity,
|
||||
compare_noop
|
||||
),
|
||||
[],
|
||||
(
|
||||
EmacsField::Required(":value"),
|
||||
|r| Some(r.value),
|
||||
@@ -2513,16 +2119,7 @@ fn compare_latex_environment<'b, 's>(
|
||||
source,
|
||||
emacs,
|
||||
rust,
|
||||
(
|
||||
EmacsField::Optional(":name"),
|
||||
|r| r.name,
|
||||
compare_property_quoted_string
|
||||
),
|
||||
(
|
||||
EmacsField::Optional(":caption"),
|
||||
compare_identity,
|
||||
compare_noop
|
||||
),
|
||||
[],
|
||||
(
|
||||
EmacsField::Required(":value"),
|
||||
|r| Some(r.value),
|
||||
|
||||
@@ -30,7 +30,7 @@
|
||||
/// }
|
||||
/// ```
|
||||
macro_rules! compare_properties {
|
||||
($source:expr, $emacs:expr, $rust:expr, $(($emacs_field:expr, $rust_value_getter:expr, $compare_fn: expr)),+) => {
|
||||
($source:expr, $emacs:expr, $rust:expr, $(($emacs_field:expr, $rust_value_getter:expr, $compare_fn: expr)),*) => {
|
||||
{
|
||||
let mut new_status = Vec::new();
|
||||
let children = $emacs.as_list()?;
|
||||
@@ -63,7 +63,7 @@ macro_rules! compare_properties {
|
||||
},
|
||||
EmacsField::Optional(_name) => {},
|
||||
}
|
||||
)+
|
||||
)*
|
||||
|
||||
if !emacs_keys.is_empty() {
|
||||
let unexpected_keys: Vec<&str> = emacs_keys.into_iter().collect();
|
||||
@@ -91,7 +91,180 @@ macro_rules! compare_properties {
|
||||
new_status.push(result);
|
||||
}
|
||||
}
|
||||
)+
|
||||
)*
|
||||
|
||||
new_status
|
||||
}
|
||||
};
|
||||
// For elements with affiliated keywords
|
||||
($source:expr, $emacs:expr, $rust:expr, [], $(($emacs_field:expr, $rust_value_getter:expr, $compare_fn: expr)),*) => {
|
||||
{
|
||||
let mut new_status = Vec::new();
|
||||
let children = $emacs.as_list()?;
|
||||
let attributes_child = children
|
||||
.iter()
|
||||
.nth(1)
|
||||
.ok_or("Should have an attributes child.")?;
|
||||
let attributes_map = attributes_child.as_map()?;
|
||||
let mut emacs_keys: BTreeSet<&str> = attributes_map.keys().map(|s| *s).collect();
|
||||
if emacs_keys.contains(":standard-properties") {
|
||||
emacs_keys.remove(":standard-properties");
|
||||
} else {
|
||||
new_status.push(ComparePropertiesResult::SelfChange(DiffStatus::Bad, Some(format!(
|
||||
"Emacs token lacks :standard-properties field.",
|
||||
))));
|
||||
}
|
||||
let affiliated_keywords_names: Vec<String> = affiliated_keywords_names($rust).collect();
|
||||
for additional_field in affiliated_keywords_names.iter().map(String::as_str).map(EmacsField::Required) {
|
||||
match additional_field {
|
||||
EmacsField::Required(name) if emacs_keys.contains(name) => {
|
||||
emacs_keys.remove(name);
|
||||
},
|
||||
EmacsField::Optional(name) if emacs_keys.contains(name) => {
|
||||
emacs_keys.remove(name);
|
||||
},
|
||||
EmacsField::Required(name) => {
|
||||
new_status.push(ComparePropertiesResult::SelfChange(DiffStatus::Bad, Some(format!(
|
||||
"Emacs token lacks required field: {}",
|
||||
name
|
||||
))));
|
||||
},
|
||||
EmacsField::Optional(_name) => {},
|
||||
}
|
||||
}
|
||||
$(
|
||||
match $emacs_field {
|
||||
EmacsField::Required(name) if emacs_keys.contains(name) => {
|
||||
emacs_keys.remove(name);
|
||||
},
|
||||
EmacsField::Optional(name) if emacs_keys.contains(name) => {
|
||||
emacs_keys.remove(name);
|
||||
},
|
||||
EmacsField::Required(name) => {
|
||||
new_status.push(ComparePropertiesResult::SelfChange(DiffStatus::Bad, Some(format!(
|
||||
"Emacs token lacks required field: {}",
|
||||
name
|
||||
))));
|
||||
},
|
||||
EmacsField::Optional(_name) => {},
|
||||
}
|
||||
)*
|
||||
|
||||
if !emacs_keys.is_empty() {
|
||||
let unexpected_keys: Vec<&str> = emacs_keys.into_iter().collect();
|
||||
let unexpected_keys = unexpected_keys.join(", ");
|
||||
new_status.push(ComparePropertiesResult::SelfChange(DiffStatus::Bad, Some(format!(
|
||||
"Emacs token had extra field(s): {}",
|
||||
unexpected_keys
|
||||
))));
|
||||
}
|
||||
|
||||
for diff in compare_affiliated_keywords($source, $emacs, $rust)? {
|
||||
new_status.push(diff);
|
||||
}
|
||||
|
||||
$(
|
||||
let emacs_name = match $emacs_field {
|
||||
EmacsField::Required(name) => {
|
||||
name
|
||||
},
|
||||
EmacsField::Optional(name) => {
|
||||
name
|
||||
},
|
||||
};
|
||||
let result = $compare_fn($source, $emacs, $rust, emacs_name, $rust_value_getter)?;
|
||||
match result {
|
||||
ComparePropertiesResult::SelfChange(DiffStatus::Good, _) => unreachable!("No comparison functions should return SelfChange() when DiffStatus is good."),
|
||||
ComparePropertiesResult::NoChange => {},
|
||||
result => {
|
||||
new_status.push(result);
|
||||
}
|
||||
}
|
||||
)*
|
||||
|
||||
new_status
|
||||
}
|
||||
};
|
||||
// Specifies additional properties
|
||||
($source:expr, $emacs:expr, $rust:expr, $additionalproperties: expr, $(($emacs_field:expr, $rust_value_getter:expr, $compare_fn: expr)),*) => {
|
||||
{
|
||||
let mut new_status = Vec::new();
|
||||
let children = $emacs.as_list()?;
|
||||
let attributes_child = children
|
||||
.iter()
|
||||
.nth(1)
|
||||
.ok_or("Should have an attributes child.")?;
|
||||
let attributes_map = attributes_child.as_map()?;
|
||||
let mut emacs_keys: BTreeSet<&str> = attributes_map.keys().map(|s| *s).collect();
|
||||
if emacs_keys.contains(":standard-properties") {
|
||||
emacs_keys.remove(":standard-properties");
|
||||
} else {
|
||||
new_status.push(ComparePropertiesResult::SelfChange(DiffStatus::Bad, Some(format!(
|
||||
"Emacs token lacks :standard-properties field.",
|
||||
))));
|
||||
}
|
||||
for additional_field in $additionalproperties {
|
||||
match additional_field {
|
||||
EmacsField::Required(name) if emacs_keys.contains(name) => {
|
||||
emacs_keys.remove(name);
|
||||
},
|
||||
EmacsField::Optional(name) if emacs_keys.contains(name) => {
|
||||
emacs_keys.remove(name);
|
||||
},
|
||||
EmacsField::Required(name) => {
|
||||
new_status.push(ComparePropertiesResult::SelfChange(DiffStatus::Bad, Some(format!(
|
||||
"Emacs token lacks required field: {}",
|
||||
name
|
||||
))));
|
||||
},
|
||||
EmacsField::Optional(_name) => {},
|
||||
}
|
||||
}
|
||||
$(
|
||||
match $emacs_field {
|
||||
EmacsField::Required(name) if emacs_keys.contains(name) => {
|
||||
emacs_keys.remove(name);
|
||||
},
|
||||
EmacsField::Optional(name) if emacs_keys.contains(name) => {
|
||||
emacs_keys.remove(name);
|
||||
},
|
||||
EmacsField::Required(name) => {
|
||||
new_status.push(ComparePropertiesResult::SelfChange(DiffStatus::Bad, Some(format!(
|
||||
"Emacs token lacks required field: {}",
|
||||
name
|
||||
))));
|
||||
},
|
||||
EmacsField::Optional(_name) => {},
|
||||
}
|
||||
)*
|
||||
|
||||
if !emacs_keys.is_empty() {
|
||||
let unexpected_keys: Vec<&str> = emacs_keys.into_iter().collect();
|
||||
let unexpected_keys = unexpected_keys.join(", ");
|
||||
new_status.push(ComparePropertiesResult::SelfChange(DiffStatus::Bad, Some(format!(
|
||||
"Emacs token had extra field(s): {}",
|
||||
unexpected_keys
|
||||
))));
|
||||
}
|
||||
|
||||
$(
|
||||
let emacs_name = match $emacs_field {
|
||||
EmacsField::Required(name) => {
|
||||
name
|
||||
},
|
||||
EmacsField::Optional(name) => {
|
||||
name
|
||||
},
|
||||
};
|
||||
let result = $compare_fn($source, $emacs, $rust, emacs_name, $rust_value_getter)?;
|
||||
match result {
|
||||
ComparePropertiesResult::SelfChange(DiffStatus::Good, _) => unreachable!("No comparison functions should return SelfChange() when DiffStatus is good."),
|
||||
ComparePropertiesResult::NoChange => {},
|
||||
result => {
|
||||
new_status.push(result);
|
||||
}
|
||||
}
|
||||
)*
|
||||
|
||||
new_status
|
||||
}
|
||||
|
||||
@@ -10,3 +10,5 @@ pub use compare::run_anonymous_compare;
|
||||
pub use compare::run_anonymous_compare_with_settings;
|
||||
pub use compare::run_compare_on_file;
|
||||
pub use compare::run_compare_on_file_with_settings;
|
||||
pub use compare::silent_anonymous_compare;
|
||||
pub use compare::silent_compare_on_file;
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
use std::path::Path;
|
||||
use std::process::Command;
|
||||
|
||||
use tokio::process::Command;
|
||||
|
||||
use crate::context::HeadlineLevelFilter;
|
||||
use crate::settings::GlobalSettings;
|
||||
@@ -25,9 +26,9 @@ fn global_settings_elisp(global_settings: &GlobalSettings) -> String {
|
||||
ret
|
||||
}
|
||||
|
||||
pub(crate) fn emacs_parse_anonymous_org_document<C>(
|
||||
pub(crate) async fn emacs_parse_anonymous_org_document<'g, 's, C>(
|
||||
file_contents: C,
|
||||
global_settings: &GlobalSettings,
|
||||
global_settings: &GlobalSettings<'g, 's>,
|
||||
) -> Result<String, Box<dyn std::error::Error>>
|
||||
where
|
||||
C: AsRef<str>,
|
||||
@@ -54,15 +55,24 @@ where
|
||||
.arg("--batch")
|
||||
.arg("--eval")
|
||||
.arg(elisp_script);
|
||||
let out = cmd.output()?;
|
||||
out.status.exit_ok()?;
|
||||
let out = cmd.output().await?;
|
||||
let status = out.status.exit_ok();
|
||||
if status.is_err() {
|
||||
eprintln!(
|
||||
"Emacs errored out: {}\n{}",
|
||||
String::from_utf8(out.stdout)?,
|
||||
String::from_utf8(out.stderr)?
|
||||
);
|
||||
status?;
|
||||
unreachable!();
|
||||
}
|
||||
let org_sexp = out.stderr;
|
||||
Ok(String::from_utf8(org_sexp)?)
|
||||
}
|
||||
|
||||
pub(crate) fn emacs_parse_file_org_document<P>(
|
||||
pub(crate) async fn emacs_parse_file_org_document<'g, 's, P>(
|
||||
file_path: P,
|
||||
global_settings: &GlobalSettings,
|
||||
global_settings: &GlobalSettings<'g, 's>,
|
||||
) -> Result<String, Box<dyn std::error::Error>>
|
||||
where
|
||||
P: AsRef<Path>,
|
||||
@@ -97,8 +107,17 @@ where
|
||||
.arg("--batch")
|
||||
.arg("--eval")
|
||||
.arg(elisp_script);
|
||||
let out = cmd.output()?;
|
||||
out.status.exit_ok()?;
|
||||
let out = cmd.output().await?;
|
||||
let status = out.status.exit_ok();
|
||||
if status.is_err() {
|
||||
eprintln!(
|
||||
"Emacs errored out: {}\n{}",
|
||||
String::from_utf8(out.stdout)?,
|
||||
String::from_utf8(out.stderr)?
|
||||
);
|
||||
status?;
|
||||
unreachable!();
|
||||
}
|
||||
let org_sexp = out.stderr;
|
||||
Ok(String::from_utf8(org_sexp)?)
|
||||
}
|
||||
@@ -125,7 +144,7 @@ where
|
||||
output
|
||||
}
|
||||
|
||||
pub fn get_emacs_version() -> Result<String, Box<dyn std::error::Error>> {
|
||||
pub async fn get_emacs_version() -> Result<String, Box<dyn std::error::Error>> {
|
||||
let elisp_script = r#"(progn
|
||||
(message "%s" (version))
|
||||
)"#;
|
||||
@@ -138,12 +157,12 @@ pub fn get_emacs_version() -> Result<String, Box<dyn std::error::Error>> {
|
||||
.arg("--eval")
|
||||
.arg(elisp_script);
|
||||
|
||||
let out = cmd.output()?;
|
||||
let out = cmd.output().await?;
|
||||
out.status.exit_ok()?;
|
||||
Ok(String::from_utf8(out.stderr)?)
|
||||
}
|
||||
|
||||
pub fn get_org_mode_version() -> Result<String, Box<dyn std::error::Error>> {
|
||||
pub async fn get_org_mode_version() -> Result<String, Box<dyn std::error::Error>> {
|
||||
let elisp_script = r#"(progn
|
||||
(org-mode)
|
||||
(message "%s" (org-version nil t nil))
|
||||
@@ -157,7 +176,7 @@ pub fn get_org_mode_version() -> Result<String, Box<dyn std::error::Error>> {
|
||||
.arg("--eval")
|
||||
.arg(elisp_script);
|
||||
|
||||
let out = cmd.output()?;
|
||||
let out = cmd.output().await?;
|
||||
out.status.exit_ok()?;
|
||||
Ok(String::from_utf8(out.stderr)?)
|
||||
}
|
||||
|
||||
@@ -1,12 +1,18 @@
|
||||
use std::str::FromStr;
|
||||
|
||||
use super::compare_field::compare_property_list_of_list_of_list_of_ast_nodes;
|
||||
use super::compare_field::compare_property_list_of_quoted_string;
|
||||
use super::compare_field::compare_property_quoted_string;
|
||||
use super::compare_field::ComparePropertiesResult;
|
||||
use super::diff::DiffEntry;
|
||||
use super::diff::DiffStatus;
|
||||
use super::elisp_fact::GetElispFact;
|
||||
use super::sexp::Token;
|
||||
use crate::compare::diff::compare_ast_node;
|
||||
use crate::compare::sexp::unquote;
|
||||
use crate::types::AffiliatedKeywordValue;
|
||||
use crate::types::AstNode;
|
||||
use crate::types::GetAffiliatedKeywords;
|
||||
use crate::types::GetStandardProperties;
|
||||
use crate::types::StandardProperties;
|
||||
|
||||
@@ -221,22 +227,6 @@ pub(crate) fn get_property_quoted_string<'b, 's, 'x>(
|
||||
.map_or(Ok(None), |r| r.map(Some))?)
|
||||
}
|
||||
|
||||
/// Get a named property containing a boolean value.
|
||||
///
|
||||
/// This uses the elisp convention of nil == false, non-nil == true.
|
||||
///
|
||||
/// Returns false if key is not found.
|
||||
pub(crate) fn get_property_boolean<'b, 's, 'x>(
|
||||
emacs: &'b Token<'s>,
|
||||
key: &'x str,
|
||||
) -> Result<bool, Box<dyn std::error::Error>> {
|
||||
Ok(get_property(emacs, key)?
|
||||
.map(Token::as_atom)
|
||||
.map_or(Ok(None), |r| r.map(Some))?
|
||||
.unwrap_or("nil")
|
||||
!= "nil")
|
||||
}
|
||||
|
||||
/// Get a named property containing an unquoted numeric value.
|
||||
///
|
||||
/// Returns None if key is not found.
|
||||
@@ -327,3 +317,86 @@ pub(crate) fn assert_no_children<'b, 's>(
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(crate) fn compare_additional_properties<'b, 's, RK, RV, RI>(
|
||||
emacs: &'b Token<'s>,
|
||||
rust_children: RI,
|
||||
) -> Result<ComparePropertiesResult<'b, 's>, Box<dyn std::error::Error>>
|
||||
where
|
||||
RK: AsRef<str>,
|
||||
RV: AsRef<str>,
|
||||
RI: Iterator<Item = (RK, RV)> + ExactSizeIterator,
|
||||
{
|
||||
for (rust_key, rust_value) in rust_children {
|
||||
let rust_key = rust_key.as_ref();
|
||||
let rust_value = rust_value.as_ref();
|
||||
let emacs_value = get_property_quoted_string(emacs, rust_key)?;
|
||||
if Some(rust_value) != emacs_value.as_ref().map(String::as_str) {
|
||||
let this_status = DiffStatus::Bad;
|
||||
let message = Some(format!(
|
||||
"{} mismatch (emacs != rust) {:?} != {:?}",
|
||||
rust_key, emacs_value, rust_value
|
||||
));
|
||||
return Ok(ComparePropertiesResult::SelfChange(this_status, message));
|
||||
}
|
||||
}
|
||||
Ok(ComparePropertiesResult::NoChange)
|
||||
}
|
||||
|
||||
pub(crate) fn compare_affiliated_keywords<'b, 's, GAK>(
|
||||
source: &'s str,
|
||||
emacs: &'b Token<'s>,
|
||||
rust: &'b GAK,
|
||||
) -> Result<Vec<ComparePropertiesResult<'b, 's>>, Box<dyn std::error::Error>>
|
||||
where
|
||||
GAK: GetAffiliatedKeywords<'s>,
|
||||
{
|
||||
let mut ret = Vec::new();
|
||||
let affiliated_keywords = rust.get_affiliated_keywords();
|
||||
for (rust_name, rust_value) in affiliated_keywords.keywords.iter() {
|
||||
let emacs_property_name = format!(":{}", rust_name);
|
||||
match rust_value {
|
||||
AffiliatedKeywordValue::SingleString(rust_value) => {
|
||||
let diff = compare_property_quoted_string(
|
||||
source,
|
||||
emacs,
|
||||
rust,
|
||||
emacs_property_name.as_str(),
|
||||
|_| Some(*rust_value),
|
||||
)?;
|
||||
ret.push(diff);
|
||||
}
|
||||
AffiliatedKeywordValue::ListOfStrings(rust_value) => {
|
||||
let diff = compare_property_list_of_quoted_string(
|
||||
source,
|
||||
emacs,
|
||||
rust,
|
||||
emacs_property_name.as_str(),
|
||||
|_| Some(rust_value.iter()),
|
||||
)?;
|
||||
ret.push(diff);
|
||||
}
|
||||
AffiliatedKeywordValue::ListOfListsOfObjects(rust_value) => {
|
||||
let diff = compare_property_list_of_list_of_list_of_ast_nodes(
|
||||
source,
|
||||
emacs,
|
||||
rust,
|
||||
emacs_property_name.as_str(),
|
||||
|_| Some(rust_value),
|
||||
)?;
|
||||
ret.push(diff);
|
||||
}
|
||||
};
|
||||
}
|
||||
Ok(ret)
|
||||
}
|
||||
|
||||
pub(crate) fn affiliated_keywords_names<'s, GAK>(rust: &'s GAK) -> impl Iterator<Item = String> + 's
|
||||
where
|
||||
GAK: GetAffiliatedKeywords<'s>,
|
||||
{
|
||||
rust.get_affiliated_keywords()
|
||||
.keywords
|
||||
.keys()
|
||||
.map(|k| format!(":{}", k))
|
||||
}
|
||||
|
||||
@@ -1,5 +1,25 @@
|
||||
use super::global_settings::EntityDefinition;
|
||||
|
||||
pub(crate) const DEFAULT_ORG_ELEMENT_PARSED_KEYWORDS: [&'static str; 1] = ["CAPTION"];
|
||||
|
||||
pub(crate) const DEFAULT_ORG_ELEMENT_DUAL_KEYWORDS: [&'static str; 2] = ["CAPTION", "RESULTS"];
|
||||
|
||||
pub(crate) const DEFAULT_ORG_ELEMENT_AFFILIATED_KEYWORDS: [&'static str; 13] = [
|
||||
"CAPTION", "DATA", "HEADER", "HEADERS", "LABEL", "NAME", "PLOT", "RESNAME", "RESULT",
|
||||
"RESULTS", "SOURCE", "SRCNAME", "TBLNAME",
|
||||
];
|
||||
|
||||
pub(crate) const DEFAULT_ORG_ELEMENT_KEYWORD_TRANSLATION_ALIST: [(&'static str, &'static str); 8] = [
|
||||
("DATA", "NAME"),
|
||||
("LABEL", "NAME"),
|
||||
("RESNAME", "NAME"),
|
||||
("SOURCE", "NAME"),
|
||||
("SRCNAME", "NAME"),
|
||||
("TBLNAME", "NAME"),
|
||||
("RESULT", "RESULTS"),
|
||||
("HEADERS", "HEADER"),
|
||||
];
|
||||
|
||||
pub(crate) const DEFAULT_ORG_LINK_PARAMETERS: [&'static str; 23] = [
|
||||
"id",
|
||||
"eww",
|
||||
|
||||
@@ -12,7 +12,6 @@ use crate::error::CustomError;
|
||||
use crate::error::MyError;
|
||||
use crate::error::Res;
|
||||
use crate::parser::OrgSource;
|
||||
use crate::types::Keyword;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub(crate) enum ContextElement<'r, 's> {
|
||||
@@ -25,22 +24,11 @@ pub(crate) enum ContextElement<'r, 's> {
|
||||
/// Indicates if elements should consume the whitespace after them.
|
||||
ConsumeTrailingWhitespace(bool),
|
||||
|
||||
/// Indicate that we are parsing a paragraph that already has affiliated keywords.
|
||||
///
|
||||
/// The value stored is the start of the element after the affiliated keywords. In this way, we can ensure that we do not exit an element immediately after the affiliated keyword had been consumed.
|
||||
HasAffiliatedKeyword(HasAffiliatedKeywordInner<'r, 's>),
|
||||
|
||||
/// This is just here to use the 's lifetime until I'm sure we can eliminate it from ContextElement.
|
||||
#[allow(dead_code)]
|
||||
Placeholder(PhantomData<&'s str>),
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub(crate) struct HasAffiliatedKeywordInner<'r, 's> {
|
||||
pub(crate) start_after_affiliated_keywords: OrgSource<'s>,
|
||||
pub(crate) keywords: &'r Vec<Keyword<'s>>,
|
||||
}
|
||||
|
||||
pub(crate) struct ExitMatcherNode<'r> {
|
||||
// TODO: Should this be "&'r DynContextMatcher<'c>" ?
|
||||
pub(crate) exit_matcher: &'r DynContextMatcher<'r>,
|
||||
|
||||
@@ -1,6 +1,12 @@
|
||||
use std::fmt::Debug;
|
||||
use std::path::PathBuf;
|
||||
|
||||
#[cfg(any(feature = "compare", feature = "foreign_document_test"))]
|
||||
pub trait FileAccessInterface: Sync + Debug {
|
||||
fn read_file(&self, path: &str) -> Result<String, std::io::Error>;
|
||||
}
|
||||
|
||||
#[cfg(not(any(feature = "compare", feature = "foreign_document_test")))]
|
||||
pub trait FileAccessInterface: Debug {
|
||||
fn read_file(&self, path: &str) -> Result<String, std::io::Error>;
|
||||
}
|
||||
|
||||
@@ -5,6 +5,10 @@ use super::constants::DEFAULT_ORG_ENTITIES;
|
||||
use super::constants::DEFAULT_ORG_LINK_PARAMETERS;
|
||||
use super::FileAccessInterface;
|
||||
use super::LocalFileAccessInterface;
|
||||
use crate::context::constants::DEFAULT_ORG_ELEMENT_AFFILIATED_KEYWORDS;
|
||||
use crate::context::constants::DEFAULT_ORG_ELEMENT_DUAL_KEYWORDS;
|
||||
use crate::context::constants::DEFAULT_ORG_ELEMENT_KEYWORD_TRANSLATION_ALIST;
|
||||
use crate::context::constants::DEFAULT_ORG_ELEMENT_PARSED_KEYWORDS;
|
||||
use crate::types::IndentationLevel;
|
||||
use crate::types::Object;
|
||||
|
||||
@@ -54,6 +58,26 @@ pub struct GlobalSettings<'g, 's> {
|
||||
///
|
||||
/// Corresponds to org-entities elisp variable.
|
||||
pub entities: &'g [EntityDefinition<'s>],
|
||||
|
||||
/// Keywords that contain the standard set of objects (excluding footnote references).
|
||||
///
|
||||
/// Corresponds to org-element-parsed-keywords elisp variable.
|
||||
pub element_parsed_keywords: &'g [&'s str],
|
||||
|
||||
/// Keywords that can have a secondary value in square brackets.
|
||||
///
|
||||
/// Corresponds to org-element-dual-keywords elisp variable.
|
||||
pub element_dual_keywords: &'g [&'s str],
|
||||
|
||||
/// Keywords that can be affiliated with an element.
|
||||
///
|
||||
/// Corresponds to org-element-affiliated-keywords elisp variable.
|
||||
pub element_affiliated_keywords: &'g [&'s str],
|
||||
|
||||
/// Mapping of keyword names.
|
||||
///
|
||||
/// Corresponds to org-element-keyword-translation-alist elisp variable.
|
||||
pub element_keyword_translation_alist: &'g [(&'s str, &'s str)],
|
||||
}
|
||||
|
||||
pub const DEFAULT_TAB_WIDTH: IndentationLevel = 8;
|
||||
@@ -71,6 +95,9 @@ pub struct EntityDefinition<'a> {
|
||||
|
||||
impl<'g, 's> GlobalSettings<'g, 's> {
|
||||
fn new() -> GlobalSettings<'g, 's> {
|
||||
debug_assert!(
|
||||
DEFAULT_ORG_ENTITIES.is_sorted_by(|a, b| b.name.len().partial_cmp(&a.name.len()))
|
||||
);
|
||||
GlobalSettings {
|
||||
radio_targets: Vec::new(),
|
||||
file_access: &LocalFileAccessInterface {
|
||||
@@ -85,6 +112,10 @@ impl<'g, 's> GlobalSettings<'g, 's> {
|
||||
link_parameters: &DEFAULT_ORG_LINK_PARAMETERS,
|
||||
link_templates: BTreeMap::new(),
|
||||
entities: &DEFAULT_ORG_ENTITIES,
|
||||
element_parsed_keywords: &DEFAULT_ORG_ELEMENT_PARSED_KEYWORDS,
|
||||
element_dual_keywords: &DEFAULT_ORG_ELEMENT_DUAL_KEYWORDS,
|
||||
element_affiliated_keywords: &DEFAULT_ORG_ELEMENT_AFFILIATED_KEYWORDS,
|
||||
element_keyword_translation_alist: &DEFAULT_ORG_ELEMENT_KEYWORD_TRANSLATION_ALIST,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,7 +22,6 @@ type DynMatcher<'c> = dyn Matcher + 'c;
|
||||
pub(crate) use context::Context;
|
||||
pub(crate) use context::ContextElement;
|
||||
pub(crate) use context::ExitMatcherNode;
|
||||
pub(crate) use context::HasAffiliatedKeywordInner;
|
||||
pub(crate) use exiting::ExitClass;
|
||||
pub use file_access_interface::FileAccessInterface;
|
||||
pub use file_access_interface::LocalFileAccessInterface;
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#![feature(exit_status_error)]
|
||||
#![feature(trait_alias)]
|
||||
#![feature(path_file_prefix)]
|
||||
#![feature(is_sorted)]
|
||||
// TODO: #![warn(missing_docs)]
|
||||
|
||||
#[cfg(feature = "compare")]
|
||||
|
||||
160
src/parser/affiliated_keyword.rs
Normal file
160
src/parser/affiliated_keyword.rs
Normal file
@@ -0,0 +1,160 @@
|
||||
use std::collections::BTreeMap;
|
||||
|
||||
use nom::bytes::complete::tag;
|
||||
use nom::bytes::complete::take_until;
|
||||
use nom::character::complete::anychar;
|
||||
use nom::combinator::all_consuming;
|
||||
use nom::combinator::eof;
|
||||
use nom::combinator::map;
|
||||
use nom::combinator::map_parser;
|
||||
use nom::combinator::opt;
|
||||
use nom::combinator::peek;
|
||||
use nom::combinator::recognize;
|
||||
use nom::multi::many0;
|
||||
use nom::multi::many_till;
|
||||
use nom::sequence::tuple;
|
||||
|
||||
use super::object_parser::standard_set_object;
|
||||
use super::util::confine_context;
|
||||
use crate::context::parser_with_context;
|
||||
use crate::context::Context;
|
||||
use crate::context::ContextElement;
|
||||
use crate::context::GlobalSettings;
|
||||
use crate::context::List;
|
||||
use crate::types::AffiliatedKeywordValue;
|
||||
use crate::types::AffiliatedKeywords;
|
||||
use crate::types::Keyword;
|
||||
|
||||
pub(crate) fn parse_affiliated_keywords<'g, 's, AK>(
|
||||
global_settings: &'g GlobalSettings<'g, 's>,
|
||||
input: AK,
|
||||
) -> AffiliatedKeywords<'s>
|
||||
where
|
||||
AK: IntoIterator<Item = Keyword<'s>>,
|
||||
{
|
||||
let mut ret = BTreeMap::new();
|
||||
for kw in input {
|
||||
let translated_name = translate_name(global_settings, kw.key);
|
||||
if is_single_string_keyword(global_settings, translated_name.as_str()) {
|
||||
ret.insert(
|
||||
translated_name,
|
||||
AffiliatedKeywordValue::SingleString(kw.value),
|
||||
);
|
||||
} else if is_list_of_single_string_keyword(global_settings, translated_name.as_str()) {
|
||||
let list_of_strings = ret
|
||||
.entry(translated_name)
|
||||
.or_insert_with(|| AffiliatedKeywordValue::ListOfStrings(Vec::with_capacity(1)));
|
||||
match list_of_strings {
|
||||
AffiliatedKeywordValue::ListOfStrings(list_of_strings)
|
||||
if list_of_strings.is_empty() =>
|
||||
{
|
||||
list_of_strings.push(kw.value);
|
||||
}
|
||||
AffiliatedKeywordValue::ListOfStrings(list_of_strings) => {
|
||||
list_of_strings.clear();
|
||||
list_of_strings.push(kw.value);
|
||||
}
|
||||
_ => panic!("Invalid AffiliatedKeywordValue type."),
|
||||
}
|
||||
} else if is_list_of_objects_keyword(global_settings, translated_name.as_str()) {
|
||||
let initial_context = ContextElement::document_context();
|
||||
let initial_context = Context::new(global_settings, List::new(&initial_context));
|
||||
|
||||
let (_remaining, optional_objects) = opt(all_consuming(map(
|
||||
tuple((
|
||||
take_until("["),
|
||||
tag("["),
|
||||
map_parser(
|
||||
recognize(many_till(anychar, peek(tuple((tag("]"), eof))))),
|
||||
confine_context(|i| {
|
||||
all_consuming(many0(parser_with_context!(standard_set_object)(
|
||||
&initial_context,
|
||||
)))(i)
|
||||
}),
|
||||
),
|
||||
tag("]"),
|
||||
eof,
|
||||
)),
|
||||
|(_, _, objects, _, _)| objects,
|
||||
)))(kw.key.into())
|
||||
.expect("Object parser should always succeed.");
|
||||
|
||||
// TODO: This should be omitting footnote references
|
||||
let (_remaining, objects) = all_consuming(many0(parser_with_context!(
|
||||
standard_set_object
|
||||
)(&initial_context)))(kw.value.into())
|
||||
.expect("Object parser should always succeed.");
|
||||
let list_of_lists = ret.entry(translated_name).or_insert_with(|| {
|
||||
AffiliatedKeywordValue::ListOfListsOfObjects(Vec::with_capacity(1))
|
||||
});
|
||||
match list_of_lists {
|
||||
AffiliatedKeywordValue::ListOfListsOfObjects(list_of_lists) => {
|
||||
list_of_lists.push((optional_objects, objects));
|
||||
}
|
||||
_ => panic!("Invalid AffiliatedKeywordValue type."),
|
||||
}
|
||||
} else {
|
||||
let list_of_strings = ret
|
||||
.entry(translated_name)
|
||||
.or_insert_with(|| AffiliatedKeywordValue::ListOfStrings(Vec::with_capacity(1)));
|
||||
match list_of_strings {
|
||||
AffiliatedKeywordValue::ListOfStrings(list_of_strings) => {
|
||||
list_of_strings.push(kw.value);
|
||||
}
|
||||
_ => panic!("Invalid AffiliatedKeywordValue type."),
|
||||
}
|
||||
}
|
||||
}
|
||||
AffiliatedKeywords { keywords: ret }
|
||||
}
|
||||
|
||||
fn translate_name<'g, 's>(global_settings: &'g GlobalSettings<'g, 's>, name: &'s str) -> String {
|
||||
let name_until_optval = name
|
||||
.split_once("[")
|
||||
.map(|(before, _after)| before)
|
||||
.unwrap_or(name);
|
||||
for (src, dst) in global_settings.element_keyword_translation_alist {
|
||||
if name_until_optval.eq_ignore_ascii_case(src) {
|
||||
return dst.to_lowercase();
|
||||
}
|
||||
}
|
||||
name_until_optval.to_lowercase()
|
||||
}
|
||||
|
||||
fn is_single_string_keyword<'g, 's>(
|
||||
_global_settings: &'g GlobalSettings<'g, 's>,
|
||||
name: &'s str,
|
||||
) -> bool {
|
||||
// TODO: Is this defined by an elisp variable?
|
||||
for single_string_name in ["plot", "name"] {
|
||||
if name.eq_ignore_ascii_case(single_string_name) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
false
|
||||
}
|
||||
|
||||
fn is_list_of_single_string_keyword<'g, 's>(
|
||||
_global_settings: &'g GlobalSettings<'g, 's>,
|
||||
name: &'s str,
|
||||
) -> bool {
|
||||
// TODO: Is this defined by an elisp variable?
|
||||
for single_string_name in ["results"] {
|
||||
if name.eq_ignore_ascii_case(single_string_name) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
false
|
||||
}
|
||||
|
||||
fn is_list_of_objects_keyword<'g, 's>(
|
||||
global_settings: &'g GlobalSettings<'g, 's>,
|
||||
name: &'s str,
|
||||
) -> bool {
|
||||
for parsed_keyword in global_settings.element_parsed_keywords {
|
||||
if name.eq_ignore_ascii_case(parsed_keyword) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
false
|
||||
}
|
||||
@@ -9,14 +9,12 @@ use nom::combinator::opt;
|
||||
use nom::combinator::peek;
|
||||
use nom::combinator::recognize;
|
||||
use nom::combinator::verify;
|
||||
use nom::multi::many0;
|
||||
use nom::multi::many_till;
|
||||
use nom::sequence::tuple;
|
||||
use nom::InputTake;
|
||||
|
||||
use super::keyword::affiliated_keyword;
|
||||
use super::affiliated_keyword::parse_affiliated_keywords;
|
||||
use super::org_source::BracketDepth;
|
||||
use super::util::get_name;
|
||||
use super::util::maybe_consume_trailing_whitespace_if_not_exiting;
|
||||
use super::util::start_of_line;
|
||||
use super::OrgSource;
|
||||
@@ -28,17 +26,21 @@ use crate::error::Res;
|
||||
use crate::parser::util::get_consumed;
|
||||
use crate::parser::util::org_line_ending;
|
||||
use crate::types::BabelCall;
|
||||
use crate::types::Keyword;
|
||||
|
||||
#[cfg_attr(
|
||||
feature = "tracing",
|
||||
tracing::instrument(ret, level = "debug", skip(context))
|
||||
tracing::instrument(ret, level = "debug", skip(context, affiliated_keywords))
|
||||
)]
|
||||
pub(crate) fn babel_call<'b, 'g, 'r, 's>(
|
||||
pub(crate) fn babel_call<'b, 'g, 'r, 's, AK>(
|
||||
affiliated_keywords: AK,
|
||||
remaining: OrgSource<'s>,
|
||||
context: RefContext<'b, 'g, 'r, 's>,
|
||||
input: OrgSource<'s>,
|
||||
) -> Res<OrgSource<'s>, BabelCall<'s>> {
|
||||
let (remaining, affiliated_keywords) = many0(affiliated_keyword)(input)?;
|
||||
|
||||
) -> Res<OrgSource<'s>, BabelCall<'s>>
|
||||
where
|
||||
AK: IntoIterator<Item = Keyword<'s>>,
|
||||
{
|
||||
start_of_line(remaining)?;
|
||||
let (remaining, _) = tuple((space0, tag("#+"), tag_no_case("call"), tag(":")))(remaining)?;
|
||||
|
||||
@@ -50,7 +52,10 @@ pub(crate) fn babel_call<'b, 'g, 'r, 's>(
|
||||
remaining,
|
||||
BabelCall {
|
||||
source: Into::<&str>::into(source),
|
||||
name: get_name(&affiliated_keywords),
|
||||
affiliated_keywords: parse_affiliated_keywords(
|
||||
context.get_global_settings(),
|
||||
affiliated_keywords,
|
||||
),
|
||||
value: Into::<&str>::into(line_break.take(0)),
|
||||
call: None,
|
||||
inside_header: None,
|
||||
@@ -73,7 +78,10 @@ pub(crate) fn babel_call<'b, 'g, 'r, 's>(
|
||||
remaining,
|
||||
BabelCall {
|
||||
source: Into::<&str>::into(source),
|
||||
name: get_name(&affiliated_keywords),
|
||||
affiliated_keywords: parse_affiliated_keywords(
|
||||
context.get_global_settings(),
|
||||
affiliated_keywords,
|
||||
),
|
||||
value: Into::<&str>::into(value).trim_end(),
|
||||
call: call.map(Into::<&str>::into),
|
||||
inside_header: inside_header.map(Into::<&str>::into),
|
||||
|
||||
@@ -4,9 +4,9 @@ use nom::combinator::recognize;
|
||||
use nom::multi::many0;
|
||||
use nom::sequence::tuple;
|
||||
|
||||
use super::affiliated_keyword::parse_affiliated_keywords;
|
||||
use super::keyword::affiliated_keyword;
|
||||
use super::org_source::OrgSource;
|
||||
use super::util::get_name;
|
||||
use super::util::maybe_consume_trailing_whitespace_if_not_exiting;
|
||||
use super::util::org_line_ending;
|
||||
use crate::context::RefContext;
|
||||
@@ -14,16 +14,21 @@ use crate::error::Res;
|
||||
use crate::parser::util::get_consumed;
|
||||
use crate::parser::util::start_of_line;
|
||||
use crate::types::DiarySexp;
|
||||
use crate::types::Keyword;
|
||||
|
||||
#[cfg_attr(
|
||||
feature = "tracing",
|
||||
tracing::instrument(ret, level = "debug", skip(context))
|
||||
tracing::instrument(ret, level = "debug", skip(context, affiliated_keywords))
|
||||
)]
|
||||
pub(crate) fn diary_sexp<'b, 'g, 'r, 's>(
|
||||
pub(crate) fn diary_sexp<'b, 'g, 'r, 's, AK>(
|
||||
affiliated_keywords: AK,
|
||||
remaining: OrgSource<'s>,
|
||||
context: RefContext<'b, 'g, 'r, 's>,
|
||||
input: OrgSource<'s>,
|
||||
) -> Res<OrgSource<'s>, DiarySexp<'s>> {
|
||||
let (remaining, affiliated_keywords) = many0(affiliated_keyword)(input)?;
|
||||
) -> Res<OrgSource<'s>, DiarySexp<'s>>
|
||||
where
|
||||
AK: IntoIterator<Item = Keyword<'s>>,
|
||||
{
|
||||
start_of_line(remaining)?;
|
||||
let (remaining, value) = recognize(tuple((tag("%%("), is_not("\r\n"))))(remaining)?;
|
||||
let (remaining, _eol) = org_line_ending(remaining)?;
|
||||
@@ -35,7 +40,10 @@ pub(crate) fn diary_sexp<'b, 'g, 'r, 's>(
|
||||
remaining,
|
||||
DiarySexp {
|
||||
source: source.into(),
|
||||
name: get_name(&affiliated_keywords),
|
||||
affiliated_keywords: parse_affiliated_keywords(
|
||||
context.get_global_settings(),
|
||||
affiliated_keywords,
|
||||
),
|
||||
value: Into::<&str>::into(value),
|
||||
},
|
||||
))
|
||||
|
||||
@@ -7,13 +7,11 @@ use nom::character::complete::space0;
|
||||
use nom::combinator::eof;
|
||||
use nom::combinator::not;
|
||||
use nom::combinator::recognize;
|
||||
use nom::multi::many0;
|
||||
use nom::multi::many_till;
|
||||
use nom::sequence::tuple;
|
||||
|
||||
use super::keyword::affiliated_keyword;
|
||||
use super::affiliated_keyword::parse_affiliated_keywords;
|
||||
use super::org_source::OrgSource;
|
||||
use super::util::get_name;
|
||||
use super::util::maybe_consume_trailing_whitespace_if_not_exiting;
|
||||
use crate::context::parser_with_context;
|
||||
use crate::context::ContextElement;
|
||||
@@ -32,23 +30,28 @@ use crate::parser::util::start_of_line;
|
||||
use crate::parser::util::WORD_CONSTITUENT_CHARACTERS;
|
||||
use crate::types::Drawer;
|
||||
use crate::types::Element;
|
||||
use crate::types::Keyword;
|
||||
use crate::types::Paragraph;
|
||||
use crate::types::SetSource;
|
||||
|
||||
#[cfg_attr(
|
||||
feature = "tracing",
|
||||
tracing::instrument(ret, level = "debug", skip(context))
|
||||
tracing::instrument(ret, level = "debug", skip(context, affiliated_keywords))
|
||||
)]
|
||||
pub(crate) fn drawer<'b, 'g, 'r, 's>(
|
||||
pub(crate) fn drawer<'b, 'g, 'r, 's, AK>(
|
||||
affiliated_keywords: AK,
|
||||
remaining: OrgSource<'s>,
|
||||
context: RefContext<'b, 'g, 'r, 's>,
|
||||
input: OrgSource<'s>,
|
||||
) -> Res<OrgSource<'s>, Drawer<'s>> {
|
||||
) -> Res<OrgSource<'s>, Drawer<'s>>
|
||||
where
|
||||
AK: IntoIterator<Item = Keyword<'s>>,
|
||||
{
|
||||
if immediate_in_section(context, "drawer") {
|
||||
return Err(nom::Err::Error(CustomError::MyError(MyError(
|
||||
"Cannot nest objects of the same element".into(),
|
||||
))));
|
||||
}
|
||||
let (remaining, affiliated_keywords) = many0(affiliated_keyword)(input)?;
|
||||
start_of_line(remaining)?;
|
||||
let (remaining, _leading_whitespace) = space0(remaining)?;
|
||||
let (remaining, (_open_colon, drawer_name, _close_colon, _new_line)) = tuple((
|
||||
@@ -100,7 +103,10 @@ pub(crate) fn drawer<'b, 'g, 'r, 's>(
|
||||
remaining,
|
||||
Drawer {
|
||||
source: source.into(),
|
||||
name: get_name(&affiliated_keywords),
|
||||
affiliated_keywords: parse_affiliated_keywords(
|
||||
context.get_global_settings(),
|
||||
affiliated_keywords,
|
||||
),
|
||||
drawer_name: drawer_name.into(),
|
||||
children,
|
||||
},
|
||||
|
||||
@@ -17,9 +17,8 @@ use nom::multi::many_till;
|
||||
use nom::sequence::preceded;
|
||||
use nom::sequence::tuple;
|
||||
|
||||
use super::keyword::affiliated_keyword;
|
||||
use super::affiliated_keyword::parse_affiliated_keywords;
|
||||
use super::org_source::OrgSource;
|
||||
use super::util::get_name;
|
||||
use super::util::maybe_consume_trailing_whitespace_if_not_exiting;
|
||||
use crate::context::parser_with_context;
|
||||
use crate::context::ContextElement;
|
||||
@@ -37,23 +36,28 @@ use crate::parser::util::immediate_in_section;
|
||||
use crate::parser::util::start_of_line;
|
||||
use crate::types::DynamicBlock;
|
||||
use crate::types::Element;
|
||||
use crate::types::Keyword;
|
||||
use crate::types::Paragraph;
|
||||
use crate::types::SetSource;
|
||||
|
||||
#[cfg_attr(
|
||||
feature = "tracing",
|
||||
tracing::instrument(ret, level = "debug", skip(context))
|
||||
tracing::instrument(ret, level = "debug", skip(context, affiliated_keywords))
|
||||
)]
|
||||
pub(crate) fn dynamic_block<'b, 'g, 'r, 's>(
|
||||
pub(crate) fn dynamic_block<'b, 'g, 'r, 's, AK>(
|
||||
affiliated_keywords: AK,
|
||||
remaining: OrgSource<'s>,
|
||||
context: RefContext<'b, 'g, 'r, 's>,
|
||||
input: OrgSource<'s>,
|
||||
) -> Res<OrgSource<'s>, DynamicBlock<'s>> {
|
||||
) -> Res<OrgSource<'s>, DynamicBlock<'s>>
|
||||
where
|
||||
AK: IntoIterator<Item = Keyword<'s>>,
|
||||
{
|
||||
if immediate_in_section(context, "dynamic block") {
|
||||
return Err(nom::Err::Error(CustomError::MyError(MyError(
|
||||
"Cannot nest objects of the same element".into(),
|
||||
))));
|
||||
}
|
||||
let (remaining, affiliated_keywords) = many0(affiliated_keyword)(input)?;
|
||||
|
||||
start_of_line(remaining)?;
|
||||
let (remaining, _leading_whitespace) = space0(remaining)?;
|
||||
@@ -107,7 +111,10 @@ pub(crate) fn dynamic_block<'b, 'g, 'r, 's>(
|
||||
remaining,
|
||||
DynamicBlock {
|
||||
source: source.into(),
|
||||
name: get_name(&affiliated_keywords),
|
||||
affiliated_keywords: parse_affiliated_keywords(
|
||||
context.get_global_settings(),
|
||||
affiliated_keywords,
|
||||
),
|
||||
block_name: name.into(),
|
||||
parameters: parameters.map(|val| val.into()),
|
||||
children,
|
||||
|
||||
@@ -1,10 +1,5 @@
|
||||
use nom::branch::alt;
|
||||
use nom::combinator::map;
|
||||
use nom::combinator::opt;
|
||||
use nom::combinator::peek;
|
||||
use nom::sequence::tuple;
|
||||
#[cfg(feature = "tracing")]
|
||||
use tracing::span;
|
||||
use nom::multi::many0;
|
||||
|
||||
use super::babel_call::babel_call;
|
||||
use super::clock::clock;
|
||||
@@ -21,7 +16,6 @@ use super::footnote_definition::footnote_definition;
|
||||
use super::greater_block::greater_block;
|
||||
use super::horizontal_rule::horizontal_rule;
|
||||
use super::keyword::affiliated_keyword;
|
||||
use super::keyword::affiliated_keyword_as_regular_keyword;
|
||||
use super::keyword::keyword;
|
||||
use super::latex_environment::latex_environment;
|
||||
use super::lesser_block::comment_block;
|
||||
@@ -39,6 +33,8 @@ use crate::context::RefContext;
|
||||
use crate::error::CustomError;
|
||||
use crate::error::MyError;
|
||||
use crate::error::Res;
|
||||
use crate::parser::macros::ak_element;
|
||||
use crate::parser::macros::element;
|
||||
use crate::parser::table::org_mode_table;
|
||||
use crate::types::Element;
|
||||
|
||||
@@ -60,117 +56,204 @@ fn _element<'b, 'g, 'r, 's>(
|
||||
input: OrgSource<'s>,
|
||||
can_be_paragraph: bool,
|
||||
) -> Res<OrgSource<'s>, Element<'s>> {
|
||||
let plain_list_matcher = parser_with_context!(plain_list)(context);
|
||||
let greater_block_matcher = parser_with_context!(greater_block)(context);
|
||||
let dynamic_block_matcher = parser_with_context!(dynamic_block)(context);
|
||||
let footnote_definition_matcher = parser_with_context!(footnote_definition)(context);
|
||||
let comment_matcher = parser_with_context!(comment)(context);
|
||||
let drawer_matcher = parser_with_context!(drawer)(context);
|
||||
let table_matcher = parser_with_context!(org_mode_table)(context);
|
||||
let verse_block_matcher = parser_with_context!(verse_block)(context);
|
||||
let comment_block_matcher = parser_with_context!(comment_block)(context);
|
||||
let example_block_matcher = parser_with_context!(example_block)(context);
|
||||
let export_block_matcher = parser_with_context!(export_block)(context);
|
||||
let src_block_matcher = parser_with_context!(src_block)(context);
|
||||
let clock_matcher = parser_with_context!(clock)(context);
|
||||
let diary_sexp_matcher = parser_with_context!(diary_sexp)(context);
|
||||
let fixed_width_area_matcher = parser_with_context!(fixed_width_area)(context);
|
||||
let horizontal_rule_matcher = parser_with_context!(horizontal_rule)(context);
|
||||
let keyword_matcher = parser_with_context!(keyword)(context);
|
||||
let babel_keyword_matcher = parser_with_context!(babel_call)(context);
|
||||
let paragraph_matcher = parser_with_context!(paragraph)(context);
|
||||
let latex_environment_matcher = parser_with_context!(latex_environment)(context);
|
||||
let (post_affiliated_keywords_input, affiliated_keywords) = many0(affiliated_keyword)(input)?;
|
||||
|
||||
let (mut remaining, mut maybe_element) = {
|
||||
#[cfg(feature = "tracing")]
|
||||
let span = span!(tracing::Level::DEBUG, "Main element block");
|
||||
#[cfg(feature = "tracing")]
|
||||
let _enter = span.enter();
|
||||
let mut affiliated_keywords = affiliated_keywords.into_iter();
|
||||
|
||||
opt(alt((
|
||||
map(plain_list_matcher, Element::PlainList),
|
||||
greater_block_matcher,
|
||||
map(dynamic_block_matcher, Element::DynamicBlock),
|
||||
map(footnote_definition_matcher, Element::FootnoteDefinition),
|
||||
map(comment_matcher, Element::Comment),
|
||||
map(drawer_matcher, Element::Drawer),
|
||||
map(table_matcher, Element::Table),
|
||||
map(verse_block_matcher, Element::VerseBlock),
|
||||
map(comment_block_matcher, Element::CommentBlock),
|
||||
map(example_block_matcher, Element::ExampleBlock),
|
||||
map(export_block_matcher, Element::ExportBlock),
|
||||
map(src_block_matcher, Element::SrcBlock),
|
||||
map(clock_matcher, Element::Clock),
|
||||
map(diary_sexp_matcher, Element::DiarySexp),
|
||||
map(fixed_width_area_matcher, Element::FixedWidthArea),
|
||||
map(horizontal_rule_matcher, Element::HorizontalRule),
|
||||
map(latex_environment_matcher, Element::LatexEnvironment),
|
||||
map(babel_keyword_matcher, Element::BabelCall),
|
||||
map(keyword_matcher, Element::Keyword),
|
||||
)))(input)?
|
||||
};
|
||||
ak_element!(
|
||||
plain_list,
|
||||
&mut affiliated_keywords,
|
||||
post_affiliated_keywords_input,
|
||||
context,
|
||||
input,
|
||||
Element::PlainList
|
||||
);
|
||||
|
||||
if maybe_element.is_none() && can_be_paragraph {
|
||||
#[cfg(feature = "tracing")]
|
||||
let span = span!(tracing::Level::DEBUG, "Paragraph with affiliated keyword.");
|
||||
#[cfg(feature = "tracing")]
|
||||
let _enter = span.enter();
|
||||
ak_element!(
|
||||
greater_block,
|
||||
&mut affiliated_keywords,
|
||||
post_affiliated_keywords_input,
|
||||
context,
|
||||
input
|
||||
);
|
||||
|
||||
let (remain, paragraph_with_affiliated_keyword) = opt(map(
|
||||
tuple((
|
||||
peek(affiliated_keyword),
|
||||
map(paragraph_matcher, Element::Paragraph),
|
||||
)),
|
||||
|(_, paragraph)| paragraph,
|
||||
))(remaining)?;
|
||||
if paragraph_with_affiliated_keyword.is_some() {
|
||||
remaining = remain;
|
||||
maybe_element = paragraph_with_affiliated_keyword;
|
||||
}
|
||||
}
|
||||
ak_element!(
|
||||
dynamic_block,
|
||||
&mut affiliated_keywords,
|
||||
post_affiliated_keywords_input,
|
||||
context,
|
||||
input,
|
||||
Element::DynamicBlock
|
||||
);
|
||||
|
||||
if maybe_element.is_none() {
|
||||
#[cfg(feature = "tracing")]
|
||||
let span = span!(
|
||||
tracing::Level::DEBUG,
|
||||
"Affiliated keyword as regular keyword."
|
||||
ak_element!(
|
||||
footnote_definition,
|
||||
&mut affiliated_keywords,
|
||||
post_affiliated_keywords_input,
|
||||
context,
|
||||
input,
|
||||
Element::FootnoteDefinition
|
||||
);
|
||||
|
||||
element!(comment, context, input, Element::Comment);
|
||||
|
||||
ak_element!(
|
||||
drawer,
|
||||
&mut affiliated_keywords,
|
||||
post_affiliated_keywords_input,
|
||||
context,
|
||||
input,
|
||||
Element::Drawer
|
||||
);
|
||||
|
||||
ak_element!(
|
||||
org_mode_table,
|
||||
&mut affiliated_keywords,
|
||||
post_affiliated_keywords_input,
|
||||
context,
|
||||
input,
|
||||
Element::Table
|
||||
);
|
||||
|
||||
ak_element!(
|
||||
verse_block,
|
||||
&mut affiliated_keywords,
|
||||
post_affiliated_keywords_input,
|
||||
context,
|
||||
input,
|
||||
Element::VerseBlock
|
||||
);
|
||||
|
||||
ak_element!(
|
||||
comment_block,
|
||||
&mut affiliated_keywords,
|
||||
post_affiliated_keywords_input,
|
||||
context,
|
||||
input,
|
||||
Element::CommentBlock
|
||||
);
|
||||
|
||||
ak_element!(
|
||||
example_block,
|
||||
&mut affiliated_keywords,
|
||||
post_affiliated_keywords_input,
|
||||
context,
|
||||
input,
|
||||
Element::ExampleBlock
|
||||
);
|
||||
|
||||
ak_element!(
|
||||
export_block,
|
||||
&mut affiliated_keywords,
|
||||
post_affiliated_keywords_input,
|
||||
context,
|
||||
input,
|
||||
Element::ExportBlock
|
||||
);
|
||||
|
||||
ak_element!(
|
||||
src_block,
|
||||
&mut affiliated_keywords,
|
||||
post_affiliated_keywords_input,
|
||||
context,
|
||||
input,
|
||||
Element::SrcBlock
|
||||
);
|
||||
|
||||
element!(clock, context, input, Element::Clock);
|
||||
|
||||
ak_element!(
|
||||
diary_sexp,
|
||||
&mut affiliated_keywords,
|
||||
post_affiliated_keywords_input,
|
||||
context,
|
||||
input,
|
||||
Element::DiarySexp
|
||||
);
|
||||
|
||||
ak_element!(
|
||||
fixed_width_area,
|
||||
&mut affiliated_keywords,
|
||||
post_affiliated_keywords_input,
|
||||
context,
|
||||
input,
|
||||
Element::FixedWidthArea
|
||||
);
|
||||
|
||||
ak_element!(
|
||||
horizontal_rule,
|
||||
&mut affiliated_keywords,
|
||||
post_affiliated_keywords_input,
|
||||
context,
|
||||
input,
|
||||
Element::HorizontalRule
|
||||
);
|
||||
|
||||
ak_element!(
|
||||
latex_environment,
|
||||
&mut affiliated_keywords,
|
||||
post_affiliated_keywords_input,
|
||||
context,
|
||||
input,
|
||||
Element::LatexEnvironment
|
||||
);
|
||||
|
||||
ak_element!(
|
||||
babel_call,
|
||||
&mut affiliated_keywords,
|
||||
post_affiliated_keywords_input,
|
||||
context,
|
||||
input,
|
||||
Element::BabelCall
|
||||
);
|
||||
|
||||
// Keyword with affiliated keywords
|
||||
ak_element!(
|
||||
keyword,
|
||||
&mut affiliated_keywords,
|
||||
post_affiliated_keywords_input,
|
||||
context,
|
||||
input,
|
||||
Element::Keyword
|
||||
);
|
||||
|
||||
if can_be_paragraph {
|
||||
// Paragraph with affiliated keyword
|
||||
ak_element!(
|
||||
paragraph,
|
||||
&mut affiliated_keywords,
|
||||
post_affiliated_keywords_input,
|
||||
context,
|
||||
input,
|
||||
Element::Paragraph
|
||||
);
|
||||
#[cfg(feature = "tracing")]
|
||||
let _enter = span.enter();
|
||||
|
||||
let (remain, kw) = opt(map(
|
||||
parser_with_context!(affiliated_keyword_as_regular_keyword)(context),
|
||||
Element::Keyword,
|
||||
))(remaining)?;
|
||||
if kw.is_some() {
|
||||
maybe_element = kw;
|
||||
remaining = remain;
|
||||
}
|
||||
}
|
||||
|
||||
if maybe_element.is_none() && can_be_paragraph {
|
||||
#[cfg(feature = "tracing")]
|
||||
let span = span!(
|
||||
tracing::Level::DEBUG,
|
||||
"Paragraph without affiliated keyword."
|
||||
// Keyword without affiliated keywords
|
||||
ak_element!(
|
||||
keyword,
|
||||
std::iter::empty(),
|
||||
input,
|
||||
context,
|
||||
input,
|
||||
Element::Keyword
|
||||
);
|
||||
|
||||
if can_be_paragraph {
|
||||
// Paragraph without affiliated keyword
|
||||
ak_element!(
|
||||
paragraph,
|
||||
std::iter::empty(),
|
||||
input,
|
||||
context,
|
||||
input,
|
||||
Element::Paragraph
|
||||
);
|
||||
#[cfg(feature = "tracing")]
|
||||
let _enter = span.enter();
|
||||
|
||||
let (remain, paragraph_without_affiliated_keyword) =
|
||||
map(paragraph_matcher, Element::Paragraph)(remaining)?;
|
||||
remaining = remain;
|
||||
maybe_element = Some(paragraph_without_affiliated_keyword);
|
||||
}
|
||||
|
||||
if maybe_element.is_none() {
|
||||
return Err(nom::Err::Error(CustomError::MyError(MyError(
|
||||
"No element.",
|
||||
))));
|
||||
}
|
||||
let element = maybe_element.expect("The above if-statement ensures this is Some().");
|
||||
|
||||
Ok((remaining, element))
|
||||
Err(nom::Err::Error(CustomError::MyError(MyError(
|
||||
"No element.",
|
||||
))))
|
||||
}
|
||||
|
||||
pub(crate) const fn detect_element(
|
||||
|
||||
@@ -9,9 +9,9 @@ use nom::multi::many_till;
|
||||
use nom::sequence::preceded;
|
||||
use nom::sequence::tuple;
|
||||
|
||||
use super::affiliated_keyword::parse_affiliated_keywords;
|
||||
use super::keyword::affiliated_keyword;
|
||||
use super::org_source::OrgSource;
|
||||
use super::util::get_name;
|
||||
use super::util::maybe_consume_trailing_whitespace_if_not_exiting;
|
||||
use super::util::org_line_ending;
|
||||
use crate::context::parser_with_context;
|
||||
@@ -21,16 +21,21 @@ use crate::parser::util::exit_matcher_parser;
|
||||
use crate::parser::util::get_consumed;
|
||||
use crate::parser::util::start_of_line;
|
||||
use crate::types::FixedWidthArea;
|
||||
use crate::types::Keyword;
|
||||
|
||||
#[cfg_attr(
|
||||
feature = "tracing",
|
||||
tracing::instrument(ret, level = "debug", skip(context))
|
||||
tracing::instrument(ret, level = "debug", skip(context, affiliated_keywords))
|
||||
)]
|
||||
pub(crate) fn fixed_width_area<'b, 'g, 'r, 's>(
|
||||
pub(crate) fn fixed_width_area<'b, 'g, 'r, 's, AK>(
|
||||
affiliated_keywords: AK,
|
||||
remaining: OrgSource<'s>,
|
||||
context: RefContext<'b, 'g, 'r, 's>,
|
||||
input: OrgSource<'s>,
|
||||
) -> Res<OrgSource<'s>, FixedWidthArea<'s>> {
|
||||
let (remaining, affiliated_keywords) = many0(affiliated_keyword)(input)?;
|
||||
) -> Res<OrgSource<'s>, FixedWidthArea<'s>>
|
||||
where
|
||||
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 (remaining, first_line) = fixed_width_area_line_matcher(remaining)?;
|
||||
@@ -57,7 +62,10 @@ pub(crate) fn fixed_width_area<'b, 'g, 'r, 's>(
|
||||
remaining,
|
||||
FixedWidthArea {
|
||||
source: source.into(),
|
||||
name: get_name(&affiliated_keywords),
|
||||
affiliated_keywords: parse_affiliated_keywords(
|
||||
context.get_global_settings(),
|
||||
affiliated_keywords,
|
||||
),
|
||||
value,
|
||||
},
|
||||
))
|
||||
|
||||
@@ -11,9 +11,9 @@ use nom::multi::many1;
|
||||
use nom::multi::many_till;
|
||||
use nom::sequence::tuple;
|
||||
|
||||
use super::affiliated_keyword::parse_affiliated_keywords;
|
||||
use super::keyword::affiliated_keyword;
|
||||
use super::org_source::OrgSource;
|
||||
use super::util::get_name;
|
||||
use super::util::include_input;
|
||||
use super::util::maybe_consume_trailing_whitespace_if_not_exiting;
|
||||
use super::util::WORD_CONSTITUENT_CHARACTERS;
|
||||
@@ -33,21 +33,26 @@ use crate::parser::util::immediate_in_section;
|
||||
use crate::parser::util::maybe_consume_trailing_whitespace;
|
||||
use crate::parser::util::start_of_line;
|
||||
use crate::types::FootnoteDefinition;
|
||||
use crate::types::Keyword;
|
||||
|
||||
#[cfg_attr(
|
||||
feature = "tracing",
|
||||
tracing::instrument(ret, level = "debug", skip(context))
|
||||
tracing::instrument(ret, level = "debug", skip(context, affiliated_keywords))
|
||||
)]
|
||||
pub(crate) fn footnote_definition<'b, 'g, 'r, 's>(
|
||||
pub(crate) fn footnote_definition<'b, 'g, 'r, 's, AK>(
|
||||
affiliated_keywords: AK,
|
||||
remaining: OrgSource<'s>,
|
||||
context: RefContext<'b, 'g, 'r, 's>,
|
||||
input: OrgSource<'s>,
|
||||
) -> Res<OrgSource<'s>, FootnoteDefinition<'s>> {
|
||||
) -> Res<OrgSource<'s>, FootnoteDefinition<'s>>
|
||||
where
|
||||
AK: IntoIterator<Item = Keyword<'s>>,
|
||||
{
|
||||
if immediate_in_section(context, "footnote definition") {
|
||||
return Err(nom::Err::Error(CustomError::MyError(MyError(
|
||||
"Cannot nest objects of the same element".into(),
|
||||
))));
|
||||
}
|
||||
let (remaining, affiliated_keywords) = many0(affiliated_keyword)(input)?;
|
||||
start_of_line(remaining)?;
|
||||
// Cannot be indented.
|
||||
let (remaining, (_, lbl, _, _, _)) = tuple((
|
||||
@@ -94,7 +99,10 @@ pub(crate) fn footnote_definition<'b, 'g, 'r, 's>(
|
||||
remaining,
|
||||
FootnoteDefinition {
|
||||
source: source.into(),
|
||||
name: get_name(&affiliated_keywords),
|
||||
affiliated_keywords: parse_affiliated_keywords(
|
||||
context.get_global_settings(),
|
||||
affiliated_keywords,
|
||||
),
|
||||
label: lbl.into(),
|
||||
children: children.into_iter().map(|(_, item)| item).collect(),
|
||||
},
|
||||
|
||||
@@ -17,9 +17,8 @@ use nom::multi::many_till;
|
||||
use nom::sequence::preceded;
|
||||
use nom::sequence::tuple;
|
||||
|
||||
use super::keyword::affiliated_keyword;
|
||||
use super::affiliated_keyword::parse_affiliated_keywords;
|
||||
use super::org_source::OrgSource;
|
||||
use super::util::get_name;
|
||||
use super::util::in_section;
|
||||
use super::util::maybe_consume_trailing_whitespace_if_not_exiting;
|
||||
use crate::context::parser_with_context;
|
||||
@@ -46,14 +45,17 @@ use crate::types::SpecialBlock;
|
||||
|
||||
#[cfg_attr(
|
||||
feature = "tracing",
|
||||
tracing::instrument(ret, level = "debug", skip(context))
|
||||
tracing::instrument(ret, level = "debug", skip(context, affiliated_keywords))
|
||||
)]
|
||||
pub(crate) fn greater_block<'b, 'g, 'r, 's>(
|
||||
context: RefContext<'b, 'g, 'r, 's>,
|
||||
pub(crate) fn greater_block<'b, 'g, 'r, 's, AK>(
|
||||
affiliated_keywords: AK,
|
||||
input: OrgSource<'s>,
|
||||
) -> Res<OrgSource<'s>, Element<'s>> {
|
||||
let pre_affiliated_keywords_input = input;
|
||||
let (input, affiliated_keywords) = many0(affiliated_keyword)(input)?;
|
||||
context: RefContext<'b, 'g, 'r, 's>,
|
||||
pre_affiliated_keywords_input: OrgSource<'s>,
|
||||
) -> Res<OrgSource<'s>, Element<'s>>
|
||||
where
|
||||
AK: IntoIterator<Item = Keyword<'s>>,
|
||||
{
|
||||
start_of_line(input)?;
|
||||
let (remaining, _leading_whitespace) = space0(input)?;
|
||||
let (remaining, (_begin, name)) = tuple((
|
||||
@@ -71,19 +73,19 @@ pub(crate) fn greater_block<'b, 'g, 'r, 's>(
|
||||
context,
|
||||
remaining,
|
||||
pre_affiliated_keywords_input,
|
||||
&affiliated_keywords,
|
||||
affiliated_keywords,
|
||||
)?,
|
||||
"quote" => quote_block(
|
||||
context,
|
||||
remaining,
|
||||
pre_affiliated_keywords_input,
|
||||
&affiliated_keywords,
|
||||
affiliated_keywords,
|
||||
)?,
|
||||
_ => special_block(name)(
|
||||
context,
|
||||
remaining,
|
||||
pre_affiliated_keywords_input,
|
||||
&affiliated_keywords,
|
||||
affiliated_keywords,
|
||||
)?,
|
||||
};
|
||||
Ok((remaining, element))
|
||||
@@ -91,14 +93,17 @@ pub(crate) fn greater_block<'b, 'g, 'r, 's>(
|
||||
|
||||
#[cfg_attr(
|
||||
feature = "tracing",
|
||||
tracing::instrument(ret, level = "debug", skip(context))
|
||||
tracing::instrument(ret, level = "debug", skip(context, affiliated_keywords))
|
||||
)]
|
||||
fn center_block<'b, 'g, 'r, 's>(
|
||||
fn center_block<'b, 'g, 'r, 's, AK>(
|
||||
context: RefContext<'b, 'g, 'r, 's>,
|
||||
input: OrgSource<'s>,
|
||||
pre_affiliated_keywords_input: OrgSource<'s>,
|
||||
affiliated_keywords: &Vec<Keyword<'s>>,
|
||||
) -> Res<OrgSource<'s>, Element<'s>> {
|
||||
affiliated_keywords: AK,
|
||||
) -> Res<OrgSource<'s>, Element<'s>>
|
||||
where
|
||||
AK: IntoIterator<Item = Keyword<'s>>,
|
||||
{
|
||||
let (remaining, (source, children)) = greater_block_body(
|
||||
context,
|
||||
input,
|
||||
@@ -110,7 +115,10 @@ fn center_block<'b, 'g, 'r, 's>(
|
||||
remaining,
|
||||
Element::CenterBlock(CenterBlock {
|
||||
source,
|
||||
name: get_name(&affiliated_keywords),
|
||||
affiliated_keywords: parse_affiliated_keywords(
|
||||
context.get_global_settings(),
|
||||
affiliated_keywords,
|
||||
),
|
||||
children,
|
||||
}),
|
||||
))
|
||||
@@ -118,14 +126,17 @@ fn center_block<'b, 'g, 'r, 's>(
|
||||
|
||||
#[cfg_attr(
|
||||
feature = "tracing",
|
||||
tracing::instrument(ret, level = "debug", skip(context))
|
||||
tracing::instrument(ret, level = "debug", skip(context, affiliated_keywords))
|
||||
)]
|
||||
fn quote_block<'b, 'g, 'r, 's>(
|
||||
fn quote_block<'b, 'g, 'r, 's, AK>(
|
||||
context: RefContext<'b, 'g, 'r, 's>,
|
||||
input: OrgSource<'s>,
|
||||
pre_affiliated_keywords_input: OrgSource<'s>,
|
||||
affiliated_keywords: &Vec<Keyword<'s>>,
|
||||
) -> Res<OrgSource<'s>, Element<'s>> {
|
||||
affiliated_keywords: AK,
|
||||
) -> Res<OrgSource<'s>, Element<'s>>
|
||||
where
|
||||
AK: IntoIterator<Item = Keyword<'s>>,
|
||||
{
|
||||
let (remaining, (source, children)) = greater_block_body(
|
||||
context,
|
||||
input,
|
||||
@@ -137,21 +148,27 @@ fn quote_block<'b, 'g, 'r, 's>(
|
||||
remaining,
|
||||
Element::QuoteBlock(QuoteBlock {
|
||||
source,
|
||||
name: get_name(&affiliated_keywords),
|
||||
affiliated_keywords: parse_affiliated_keywords(
|
||||
context.get_global_settings(),
|
||||
affiliated_keywords,
|
||||
),
|
||||
children,
|
||||
}),
|
||||
))
|
||||
}
|
||||
|
||||
fn special_block<'s>(
|
||||
fn special_block<'s, AK>(
|
||||
name: &'s str,
|
||||
) -> impl for<'b, 'g, 'r> Fn(
|
||||
RefContext<'b, 'g, 'r, 's>,
|
||||
OrgSource<'s>,
|
||||
OrgSource<'s>,
|
||||
&Vec<Keyword<'s>>,
|
||||
AK,
|
||||
) -> Res<OrgSource<'s>, Element<'s>>
|
||||
+ 's {
|
||||
+ 's
|
||||
where
|
||||
AK: IntoIterator<Item = Keyword<'s>>,
|
||||
{
|
||||
let context_name = format!("special block {}", name);
|
||||
move |context, input, pre_affiliated_keywords_input, affiliated_keywords| {
|
||||
_special_block(
|
||||
@@ -167,16 +184,19 @@ fn special_block<'s>(
|
||||
|
||||
#[cfg_attr(
|
||||
feature = "tracing",
|
||||
tracing::instrument(ret, level = "debug", skip(context))
|
||||
tracing::instrument(ret, level = "debug", skip(context, affiliated_keywords))
|
||||
)]
|
||||
fn _special_block<'c, 'b, 'g, 'r, 's>(
|
||||
fn _special_block<'c, 'b, 'g, 'r, 's, AK>(
|
||||
context: RefContext<'b, 'g, 'r, 's>,
|
||||
input: OrgSource<'s>,
|
||||
pre_affiliated_keywords_input: OrgSource<'s>,
|
||||
name: &'s str,
|
||||
context_name: &'c str,
|
||||
affiliated_keywords: &Vec<Keyword<'s>>,
|
||||
) -> Res<OrgSource<'s>, Element<'s>> {
|
||||
affiliated_keywords: AK,
|
||||
) -> Res<OrgSource<'s>, Element<'s>>
|
||||
where
|
||||
AK: IntoIterator<Item = Keyword<'s>>,
|
||||
{
|
||||
let (remaining, parameters) = opt(tuple((space1, parameters)))(input)?;
|
||||
let (remaining, (source, children)) = greater_block_body(
|
||||
context,
|
||||
@@ -189,7 +209,10 @@ fn _special_block<'c, 'b, 'g, 'r, 's>(
|
||||
remaining,
|
||||
Element::SpecialBlock(SpecialBlock {
|
||||
source,
|
||||
name: get_name(&affiliated_keywords),
|
||||
affiliated_keywords: parse_affiliated_keywords(
|
||||
context.get_global_settings(),
|
||||
affiliated_keywords,
|
||||
),
|
||||
children,
|
||||
block_type: name,
|
||||
parameters: parameters.map(|(_, parameters)| Into::<&str>::into(parameters)),
|
||||
|
||||
@@ -5,29 +5,32 @@ use nom::character::complete::space0;
|
||||
use nom::combinator::eof;
|
||||
use nom::combinator::recognize;
|
||||
use nom::combinator::verify;
|
||||
use nom::multi::many0;
|
||||
use nom::multi::many1_count;
|
||||
use nom::sequence::tuple;
|
||||
|
||||
use super::keyword::affiliated_keyword;
|
||||
use super::affiliated_keyword::parse_affiliated_keywords;
|
||||
use super::org_source::OrgSource;
|
||||
use super::util::get_consumed;
|
||||
use super::util::get_name;
|
||||
use super::util::maybe_consume_trailing_whitespace_if_not_exiting;
|
||||
use crate::context::RefContext;
|
||||
use crate::error::Res;
|
||||
use crate::parser::util::start_of_line;
|
||||
use crate::types::HorizontalRule;
|
||||
use crate::types::Keyword;
|
||||
|
||||
#[cfg_attr(
|
||||
feature = "tracing",
|
||||
tracing::instrument(ret, level = "debug", skip(context))
|
||||
tracing::instrument(ret, level = "debug", skip(context, affiliated_keywords))
|
||||
)]
|
||||
pub(crate) fn horizontal_rule<'b, 'g, 'r, 's>(
|
||||
pub(crate) fn horizontal_rule<'b, 'g, 'r, 's, AK>(
|
||||
affiliated_keywords: AK,
|
||||
remaining: OrgSource<'s>,
|
||||
context: RefContext<'b, 'g, 'r, 's>,
|
||||
input: OrgSource<'s>,
|
||||
) -> Res<OrgSource<'s>, HorizontalRule<'s>> {
|
||||
let (remaining, affiliated_keywords) = many0(affiliated_keyword)(input)?;
|
||||
) -> Res<OrgSource<'s>, HorizontalRule<'s>>
|
||||
where
|
||||
AK: IntoIterator<Item = Keyword<'s>>,
|
||||
{
|
||||
start_of_line(remaining)?;
|
||||
let (remaining, _rule) = recognize(tuple((
|
||||
space0,
|
||||
@@ -42,7 +45,10 @@ pub(crate) fn horizontal_rule<'b, 'g, 'r, 's>(
|
||||
remaining,
|
||||
HorizontalRule {
|
||||
source: source.into(),
|
||||
name: get_name(&affiliated_keywords),
|
||||
affiliated_keywords: parse_affiliated_keywords(
|
||||
context.get_global_settings(),
|
||||
affiliated_keywords,
|
||||
),
|
||||
},
|
||||
))
|
||||
}
|
||||
|
||||
@@ -13,14 +13,13 @@ use nom::combinator::not;
|
||||
use nom::combinator::peek;
|
||||
use nom::combinator::recognize;
|
||||
use nom::combinator::verify;
|
||||
use nom::multi::many0;
|
||||
use nom::multi::many_till;
|
||||
use nom::sequence::tuple;
|
||||
|
||||
use super::affiliated_keyword::parse_affiliated_keywords;
|
||||
use super::org_source::BracketDepth;
|
||||
use super::org_source::OrgSource;
|
||||
use super::util::get_consumed;
|
||||
use super::util::get_name;
|
||||
use super::util::maybe_consume_trailing_whitespace_if_not_exiting;
|
||||
use crate::context::Matcher;
|
||||
use crate::context::RefContext;
|
||||
@@ -28,6 +27,7 @@ use crate::error::CustomError;
|
||||
use crate::error::MyError;
|
||||
use crate::error::Res;
|
||||
use crate::parser::util::start_of_line;
|
||||
use crate::types::AffiliatedKeywords;
|
||||
use crate::types::Keyword;
|
||||
|
||||
const ORG_ELEMENT_AFFILIATED_KEYWORDS: [&'static str; 13] = [
|
||||
@@ -64,7 +64,7 @@ fn _filtered_keyword<'s, F: Matcher>(
|
||||
remaining,
|
||||
Keyword {
|
||||
source: consumed_input.into(),
|
||||
name: None, // 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(),
|
||||
value: "".into(),
|
||||
},
|
||||
@@ -82,7 +82,7 @@ fn _filtered_keyword<'s, F: Matcher>(
|
||||
remaining,
|
||||
Keyword {
|
||||
source: consumed_input.into(),
|
||||
name: None, // 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(),
|
||||
value: parsed_value.into(),
|
||||
},
|
||||
@@ -91,34 +91,23 @@ fn _filtered_keyword<'s, F: Matcher>(
|
||||
|
||||
#[cfg_attr(
|
||||
feature = "tracing",
|
||||
tracing::instrument(ret, level = "debug", skip(context))
|
||||
tracing::instrument(ret, level = "debug", skip(context, affiliated_keywords))
|
||||
)]
|
||||
pub(crate) fn keyword<'b, 'g, 'r, 's>(
|
||||
pub(crate) fn keyword<'b, 'g, 'r, 's, AK>(
|
||||
affiliated_keywords: AK,
|
||||
remaining: OrgSource<'s>,
|
||||
context: RefContext<'b, 'g, 'r, 's>,
|
||||
input: OrgSource<'s>,
|
||||
) -> Res<OrgSource<'s>, Keyword<'s>> {
|
||||
let (remaining, affiliated_keywords) = many0(affiliated_keyword)(input)?;
|
||||
) -> Res<OrgSource<'s>, Keyword<'s>>
|
||||
where
|
||||
AK: IntoIterator<Item = Keyword<'s>>,
|
||||
{
|
||||
let (remaining, mut kw) = filtered_keyword(regular_keyword_key)(remaining)?;
|
||||
let (remaining, _trailing_ws) =
|
||||
maybe_consume_trailing_whitespace_if_not_exiting(context, remaining)?;
|
||||
let source = get_consumed(input, remaining);
|
||||
kw.name = get_name(&affiliated_keywords);
|
||||
kw.source = Into::<&str>::into(source);
|
||||
Ok((remaining, kw))
|
||||
}
|
||||
|
||||
#[cfg_attr(
|
||||
feature = "tracing",
|
||||
tracing::instrument(ret, level = "debug", skip(context))
|
||||
)]
|
||||
pub(crate) fn affiliated_keyword_as_regular_keyword<'b, 'g, 'r, 's>(
|
||||
context: RefContext<'b, 'g, 'r, 's>,
|
||||
input: OrgSource<'s>,
|
||||
) -> Res<OrgSource<'s>, Keyword<'s>> {
|
||||
let (remaining, mut kw) = affiliated_keyword(input)?;
|
||||
let (remaining, _trailing_ws) =
|
||||
maybe_consume_trailing_whitespace_if_not_exiting(context, remaining)?;
|
||||
let source = get_consumed(input, remaining);
|
||||
kw.affiliated_keywords =
|
||||
parse_affiliated_keywords(context.get_global_settings(), affiliated_keywords);
|
||||
kw.source = Into::<&str>::into(source);
|
||||
Ok((remaining, kw))
|
||||
}
|
||||
|
||||
@@ -8,14 +8,12 @@ use nom::character::complete::space0;
|
||||
use nom::combinator::eof;
|
||||
use nom::combinator::peek;
|
||||
use nom::combinator::recognize;
|
||||
use nom::multi::many0;
|
||||
use nom::multi::many_till;
|
||||
use nom::sequence::tuple;
|
||||
|
||||
use super::keyword::affiliated_keyword;
|
||||
use super::affiliated_keyword::parse_affiliated_keywords;
|
||||
use super::org_source::OrgSource;
|
||||
use super::util::get_consumed;
|
||||
use super::util::get_name;
|
||||
use super::util::maybe_consume_trailing_whitespace_if_not_exiting;
|
||||
use crate::context::parser_with_context;
|
||||
use crate::context::ContextElement;
|
||||
@@ -26,17 +24,22 @@ use crate::context::RefContext;
|
||||
use crate::error::Res;
|
||||
use crate::parser::util::exit_matcher_parser;
|
||||
use crate::parser::util::start_of_line;
|
||||
use crate::types::Keyword;
|
||||
use crate::types::LatexEnvironment;
|
||||
|
||||
#[cfg_attr(
|
||||
feature = "tracing",
|
||||
tracing::instrument(ret, level = "debug", skip(context))
|
||||
tracing::instrument(ret, level = "debug", skip(context, affiliated_keywords))
|
||||
)]
|
||||
pub(crate) fn latex_environment<'b, 'g, 'r, 's>(
|
||||
pub(crate) fn latex_environment<'b, 'g, 'r, 's, AK>(
|
||||
affiliated_keywords: AK,
|
||||
remaining: OrgSource<'s>,
|
||||
context: RefContext<'b, 'g, 'r, 's>,
|
||||
input: OrgSource<'s>,
|
||||
) -> Res<OrgSource<'s>, LatexEnvironment<'s>> {
|
||||
let (remaining, affiliated_keywords) = many0(affiliated_keyword)(input)?;
|
||||
) -> Res<OrgSource<'s>, LatexEnvironment<'s>>
|
||||
where
|
||||
AK: IntoIterator<Item = Keyword<'s>>,
|
||||
{
|
||||
let value_start = remaining;
|
||||
start_of_line(remaining)?;
|
||||
let (remaining, _leading_whitespace) = space0(remaining)?;
|
||||
@@ -67,7 +70,10 @@ pub(crate) fn latex_environment<'b, 'g, 'r, 's>(
|
||||
remaining,
|
||||
LatexEnvironment {
|
||||
source: source.into(),
|
||||
name: get_name(&affiliated_keywords),
|
||||
affiliated_keywords: parse_affiliated_keywords(
|
||||
context.get_global_settings(),
|
||||
affiliated_keywords,
|
||||
),
|
||||
value: value.into(),
|
||||
},
|
||||
))
|
||||
|
||||
@@ -13,14 +13,12 @@ use nom::combinator::opt;
|
||||
use nom::combinator::peek;
|
||||
use nom::combinator::recognize;
|
||||
use nom::combinator::verify;
|
||||
use nom::multi::many0;
|
||||
use nom::multi::many_till;
|
||||
use nom::sequence::tuple;
|
||||
use nom::InputTake;
|
||||
|
||||
use super::keyword::affiliated_keyword;
|
||||
use super::affiliated_keyword::parse_affiliated_keywords;
|
||||
use super::org_source::OrgSource;
|
||||
use super::util::get_name;
|
||||
use super::util::maybe_consume_trailing_whitespace_if_not_exiting;
|
||||
use crate::context::parser_with_context;
|
||||
use crate::context::ContextElement;
|
||||
@@ -41,6 +39,7 @@ use crate::types::CharOffsetInLine;
|
||||
use crate::types::CommentBlock;
|
||||
use crate::types::ExampleBlock;
|
||||
use crate::types::ExportBlock;
|
||||
use crate::types::Keyword;
|
||||
use crate::types::LineNumber;
|
||||
use crate::types::Object;
|
||||
use crate::types::PlainText;
|
||||
@@ -51,13 +50,17 @@ use crate::types::VerseBlock;
|
||||
|
||||
#[cfg_attr(
|
||||
feature = "tracing",
|
||||
tracing::instrument(ret, level = "debug", skip(context))
|
||||
tracing::instrument(ret, level = "debug", skip(context, affiliated_keywords))
|
||||
)]
|
||||
pub(crate) fn verse_block<'b, 'g, 'r, 's>(
|
||||
pub(crate) fn verse_block<'b, 'g, 'r, 's, AK>(
|
||||
affiliated_keywords: AK,
|
||||
remaining: OrgSource<'s>,
|
||||
context: RefContext<'b, 'g, 'r, 's>,
|
||||
input: OrgSource<'s>,
|
||||
) -> Res<OrgSource<'s>, VerseBlock<'s>> {
|
||||
let (remaining, affiliated_keywords) = many0(affiliated_keyword)(input)?;
|
||||
) -> Res<OrgSource<'s>, VerseBlock<'s>>
|
||||
where
|
||||
AK: IntoIterator<Item = Keyword<'s>>,
|
||||
{
|
||||
let (remaining, _) = lesser_block_begin("verse")(context, remaining)?;
|
||||
let (remaining, parameters) = opt(tuple((space1, data)))(remaining)?;
|
||||
let (remaining, _nl) = recognize(tuple((space0, line_ending)))(remaining)?;
|
||||
@@ -103,7 +106,10 @@ pub(crate) fn verse_block<'b, 'g, 'r, 's>(
|
||||
remaining,
|
||||
VerseBlock {
|
||||
source: source.into(),
|
||||
name: get_name(&affiliated_keywords),
|
||||
affiliated_keywords: parse_affiliated_keywords(
|
||||
context.get_global_settings(),
|
||||
affiliated_keywords,
|
||||
),
|
||||
data: parameters.map(|parameters| Into::<&str>::into(parameters)),
|
||||
children,
|
||||
},
|
||||
@@ -112,13 +118,17 @@ pub(crate) fn verse_block<'b, 'g, 'r, 's>(
|
||||
|
||||
#[cfg_attr(
|
||||
feature = "tracing",
|
||||
tracing::instrument(ret, level = "debug", skip(context))
|
||||
tracing::instrument(ret, level = "debug", skip(context, affiliated_keywords))
|
||||
)]
|
||||
pub(crate) fn comment_block<'b, 'g, 'r, 's>(
|
||||
pub(crate) fn comment_block<'b, 'g, 'r, 's, AK>(
|
||||
affiliated_keywords: AK,
|
||||
remaining: OrgSource<'s>,
|
||||
context: RefContext<'b, 'g, 'r, 's>,
|
||||
input: OrgSource<'s>,
|
||||
) -> Res<OrgSource<'s>, CommentBlock<'s>> {
|
||||
let (remaining, affiliated_keywords) = many0(affiliated_keyword)(input)?;
|
||||
) -> Res<OrgSource<'s>, CommentBlock<'s>>
|
||||
where
|
||||
AK: IntoIterator<Item = Keyword<'s>>,
|
||||
{
|
||||
let (remaining, _) = lesser_block_begin("comment")(context, remaining)?;
|
||||
let (remaining, _parameters) = opt(tuple((space1, data)))(remaining)?;
|
||||
let (remaining, _nl) = recognize(tuple((space0, line_ending)))(remaining)?;
|
||||
@@ -145,7 +155,10 @@ pub(crate) fn comment_block<'b, 'g, 'r, 's>(
|
||||
remaining,
|
||||
CommentBlock {
|
||||
source: source.into(),
|
||||
name: get_name(&affiliated_keywords),
|
||||
affiliated_keywords: parse_affiliated_keywords(
|
||||
context.get_global_settings(),
|
||||
affiliated_keywords,
|
||||
),
|
||||
contents: contents.into(),
|
||||
},
|
||||
))
|
||||
@@ -153,13 +166,17 @@ pub(crate) fn comment_block<'b, 'g, 'r, 's>(
|
||||
|
||||
#[cfg_attr(
|
||||
feature = "tracing",
|
||||
tracing::instrument(ret, level = "debug", skip(context))
|
||||
tracing::instrument(ret, level = "debug", skip(context, affiliated_keywords))
|
||||
)]
|
||||
pub(crate) fn example_block<'b, 'g, 'r, 's>(
|
||||
pub(crate) fn example_block<'b, 'g, 'r, 's, AK>(
|
||||
affiliated_keywords: AK,
|
||||
remaining: OrgSource<'s>,
|
||||
context: RefContext<'b, 'g, 'r, 's>,
|
||||
input: OrgSource<'s>,
|
||||
) -> Res<OrgSource<'s>, ExampleBlock<'s>> {
|
||||
let (remaining, affiliated_keywords) = many0(affiliated_keyword)(input)?;
|
||||
) -> Res<OrgSource<'s>, ExampleBlock<'s>>
|
||||
where
|
||||
AK: IntoIterator<Item = Keyword<'s>>,
|
||||
{
|
||||
let (remaining, _) = lesser_block_begin("example")(context, remaining)?;
|
||||
let (remaining, parameters) = opt(alt((
|
||||
map(tuple((space1, example_switches)), |(_, switches)| switches),
|
||||
@@ -213,7 +230,10 @@ pub(crate) fn example_block<'b, 'g, 'r, 's>(
|
||||
remaining,
|
||||
ExampleBlock {
|
||||
source: source.into(),
|
||||
name: get_name(&affiliated_keywords),
|
||||
affiliated_keywords: parse_affiliated_keywords(
|
||||
context.get_global_settings(),
|
||||
affiliated_keywords,
|
||||
),
|
||||
switches,
|
||||
number_lines,
|
||||
preserve_indent,
|
||||
@@ -227,13 +247,17 @@ pub(crate) fn example_block<'b, 'g, 'r, 's>(
|
||||
|
||||
#[cfg_attr(
|
||||
feature = "tracing",
|
||||
tracing::instrument(ret, level = "debug", skip(context))
|
||||
tracing::instrument(ret, level = "debug", skip(context, affiliated_keywords))
|
||||
)]
|
||||
pub(crate) fn export_block<'b, 'g, 'r, 's>(
|
||||
pub(crate) fn export_block<'b, 'g, 'r, 's, AK>(
|
||||
affiliated_keywords: AK,
|
||||
remaining: OrgSource<'s>,
|
||||
context: RefContext<'b, 'g, 'r, 's>,
|
||||
input: OrgSource<'s>,
|
||||
) -> Res<OrgSource<'s>, ExportBlock<'s>> {
|
||||
let (remaining, affiliated_keywords) = many0(affiliated_keyword)(input)?;
|
||||
) -> Res<OrgSource<'s>, ExportBlock<'s>>
|
||||
where
|
||||
AK: IntoIterator<Item = Keyword<'s>>,
|
||||
{
|
||||
let (remaining, _) = lesser_block_begin("export")(context, remaining)?;
|
||||
// https://orgmode.org/worg/org-syntax.html#Blocks claims that export blocks must have a single word for data but testing shows no data and multi-word data still parses as an export block.
|
||||
let (remaining, export_type) = opt(map(
|
||||
@@ -266,7 +290,10 @@ pub(crate) fn export_block<'b, 'g, 'r, 's>(
|
||||
remaining,
|
||||
ExportBlock {
|
||||
source: source.into(),
|
||||
name: get_name(&affiliated_keywords),
|
||||
affiliated_keywords: parse_affiliated_keywords(
|
||||
context.get_global_settings(),
|
||||
affiliated_keywords,
|
||||
),
|
||||
export_type: export_type.map(Into::<&str>::into),
|
||||
data: parameters.map(|parameters| Into::<&str>::into(parameters)),
|
||||
contents,
|
||||
@@ -276,13 +303,17 @@ pub(crate) fn export_block<'b, 'g, 'r, 's>(
|
||||
|
||||
#[cfg_attr(
|
||||
feature = "tracing",
|
||||
tracing::instrument(ret, level = "debug", skip(context))
|
||||
tracing::instrument(ret, level = "debug", skip(context, affiliated_keywords))
|
||||
)]
|
||||
pub(crate) fn src_block<'b, 'g, 'r, 's>(
|
||||
pub(crate) fn src_block<'b, 'g, 'r, 's, AK>(
|
||||
affiliated_keywords: AK,
|
||||
remaining: OrgSource<'s>,
|
||||
context: RefContext<'b, 'g, 'r, 's>,
|
||||
input: OrgSource<'s>,
|
||||
) -> Res<OrgSource<'s>, SrcBlock<'s>> {
|
||||
let (remaining, affiliated_keywords) = many0(affiliated_keyword)(input)?;
|
||||
) -> Res<OrgSource<'s>, SrcBlock<'s>>
|
||||
where
|
||||
AK: IntoIterator<Item = Keyword<'s>>,
|
||||
{
|
||||
let (remaining, _) = lesser_block_begin("src")(context, remaining)?;
|
||||
// https://orgmode.org/worg/org-syntax.html#Blocks claims that data is mandatory and must follow the LANGUAGE SWITCHES ARGUMENTS pattern but testing has shown that no data and incorrect data here will still parse to a src block.
|
||||
let (remaining, language) =
|
||||
@@ -332,7 +363,10 @@ pub(crate) fn src_block<'b, 'g, 'r, 's>(
|
||||
remaining,
|
||||
SrcBlock {
|
||||
source: source.into(),
|
||||
name: get_name(&affiliated_keywords),
|
||||
affiliated_keywords: parse_affiliated_keywords(
|
||||
context.get_global_settings(),
|
||||
affiliated_keywords,
|
||||
),
|
||||
language: language.map(Into::<&str>::into),
|
||||
switches,
|
||||
parameters: parameters.map(Into::<&str>::into),
|
||||
|
||||
40
src/parser/macros.rs
Normal file
40
src/parser/macros.rs
Normal file
@@ -0,0 +1,40 @@
|
||||
/// Parse an element that has affiliated keywords.
|
||||
macro_rules! ak_element {
|
||||
($parser:ident, $affiliated_keywords:expr, $post_affiliated_keywords_input: expr, $context: expr, $input: expr, $wrapper: expr) => {
|
||||
if let Ok((remaining, ele)) = $parser(
|
||||
$affiliated_keywords,
|
||||
$post_affiliated_keywords_input,
|
||||
$context,
|
||||
$input,
|
||||
) {
|
||||
return Ok((remaining, $wrapper(ele)));
|
||||
}
|
||||
};
|
||||
($parser:ident, $affiliated_keywords:expr, $post_affiliated_keywords_input: expr, $context: expr, $input: expr) => {
|
||||
if let Ok((remaining, ele)) = $parser(
|
||||
$affiliated_keywords,
|
||||
$post_affiliated_keywords_input,
|
||||
$context,
|
||||
$input,
|
||||
) {
|
||||
return Ok((remaining, ele));
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
pub(crate) use ak_element;
|
||||
|
||||
macro_rules! element {
|
||||
($parser:ident, $context: expr, $input: expr, $wrapper: expr) => {
|
||||
if let Ok((remaining, ele)) = $parser($context, $input) {
|
||||
return Ok((remaining, $wrapper(ele)));
|
||||
}
|
||||
};
|
||||
($parser:ident, $context: expr, $input: expr) => {
|
||||
if let Ok((remaining, ele)) = $parser($context, $input) {
|
||||
return Ok((remaining, ele));
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
pub(crate) use element;
|
||||
@@ -1,3 +1,4 @@
|
||||
mod affiliated_keyword;
|
||||
mod angle_link;
|
||||
mod babel_call;
|
||||
mod citation;
|
||||
@@ -26,6 +27,7 @@ mod latex_environment;
|
||||
mod latex_fragment;
|
||||
mod lesser_block;
|
||||
mod line_break;
|
||||
mod macros;
|
||||
mod object_parser;
|
||||
mod org_macro;
|
||||
mod org_source;
|
||||
|
||||
@@ -2,54 +2,46 @@ use nom::branch::alt;
|
||||
use nom::combinator::eof;
|
||||
use nom::combinator::recognize;
|
||||
use nom::combinator::verify;
|
||||
use nom::multi::many0;
|
||||
use nom::multi::many1;
|
||||
use nom::multi::many_till;
|
||||
use nom::sequence::tuple;
|
||||
|
||||
use super::affiliated_keyword::parse_affiliated_keywords;
|
||||
use super::element_parser::detect_element;
|
||||
use super::keyword::affiliated_keyword;
|
||||
use super::org_source::OrgSource;
|
||||
use super::util::blank_line;
|
||||
use super::util::get_consumed;
|
||||
use super::util::get_has_affiliated_keyword;
|
||||
use super::util::get_name;
|
||||
use super::util::maybe_consume_trailing_whitespace_if_not_exiting;
|
||||
use crate::context::parser_with_context;
|
||||
use crate::context::ContextElement;
|
||||
use crate::context::ExitClass;
|
||||
use crate::context::ExitMatcherNode;
|
||||
use crate::context::HasAffiliatedKeywordInner;
|
||||
use crate::context::RefContext;
|
||||
use crate::error::CustomError;
|
||||
use crate::error::MyError;
|
||||
use crate::error::Res;
|
||||
use crate::parser::object_parser::standard_set_object;
|
||||
use crate::parser::util::exit_matcher_parser;
|
||||
use crate::parser::util::start_of_line;
|
||||
use crate::types::Keyword;
|
||||
use crate::types::Paragraph;
|
||||
|
||||
#[cfg_attr(
|
||||
feature = "tracing",
|
||||
tracing::instrument(ret, level = "debug", skip(context))
|
||||
tracing::instrument(ret, level = "debug", skip(context, affiliated_keywords))
|
||||
)]
|
||||
pub(crate) fn paragraph<'b, 'g, 'r, 's>(
|
||||
pub(crate) fn paragraph<'b, 'g, 'r, 's, AK>(
|
||||
affiliated_keywords: AK,
|
||||
remaining: OrgSource<'s>,
|
||||
context: RefContext<'b, 'g, 'r, 's>,
|
||||
input: OrgSource<'s>,
|
||||
) -> Res<OrgSource<'s>, Paragraph<'s>> {
|
||||
let (remaining, affiliated_keywords) = many0(affiliated_keyword)(input)?;
|
||||
let contexts = [
|
||||
ContextElement::HasAffiliatedKeyword(HasAffiliatedKeywordInner {
|
||||
start_after_affiliated_keywords: remaining,
|
||||
keywords: &affiliated_keywords,
|
||||
}),
|
||||
ContextElement::ExitMatcherNode(ExitMatcherNode {
|
||||
class: ExitClass::Gamma,
|
||||
exit_matcher: ¶graph_end,
|
||||
}),
|
||||
];
|
||||
) -> Res<OrgSource<'s>, Paragraph<'s>>
|
||||
where
|
||||
AK: IntoIterator<Item = Keyword<'s>>,
|
||||
{
|
||||
let contexts = [ContextElement::ExitMatcherNode(ExitMatcherNode {
|
||||
class: ExitClass::Gamma,
|
||||
exit_matcher: ¶graph_end,
|
||||
})];
|
||||
let parser_context = context.with_additional_node(&contexts[0]);
|
||||
let parser_context = parser_context.with_additional_node(&contexts[1]);
|
||||
let standard_set_object_matcher = parser_with_context!(standard_set_object)(&parser_context);
|
||||
let exit_matcher = parser_with_context!(exit_matcher_parser)(&parser_context);
|
||||
|
||||
@@ -68,7 +60,10 @@ pub(crate) fn paragraph<'b, 'g, 'r, 's>(
|
||||
remaining,
|
||||
Paragraph {
|
||||
source: source.into(),
|
||||
name: get_name(&affiliated_keywords),
|
||||
affiliated_keywords: parse_affiliated_keywords(
|
||||
context.get_global_settings(),
|
||||
affiliated_keywords,
|
||||
),
|
||||
children,
|
||||
},
|
||||
))
|
||||
@@ -86,15 +81,6 @@ fn paragraph_end<'b, 'g, 'r, 's>(
|
||||
if regular_end.is_ok() {
|
||||
return regular_end;
|
||||
}
|
||||
match get_has_affiliated_keyword(context) {
|
||||
Some(start_post_affiliated_keywords) if input == start_post_affiliated_keywords => {
|
||||
return Err(nom::Err::Error(CustomError::MyError(MyError(
|
||||
"No exit due to affiliated keywords.",
|
||||
))));
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
// Check to see if input is the start of a HasAffiliatedKeyword
|
||||
alt((
|
||||
recognize(parser_with_context!(detect_element(false))(context)),
|
||||
eof,
|
||||
|
||||
@@ -18,11 +18,11 @@ use nom::multi::many1;
|
||||
use nom::multi::many_till;
|
||||
use nom::sequence::tuple;
|
||||
|
||||
use super::affiliated_keyword::parse_affiliated_keywords;
|
||||
use super::element_parser::element;
|
||||
use super::keyword::affiliated_keyword;
|
||||
use super::object_parser::standard_set_object;
|
||||
use super::org_source::OrgSource;
|
||||
use super::util::get_name;
|
||||
use super::util::include_input;
|
||||
use super::util::indentation_level;
|
||||
use super::util::non_whitespace_character;
|
||||
@@ -43,6 +43,7 @@ use crate::parser::util::org_space;
|
||||
use crate::parser::util::start_of_line;
|
||||
use crate::types::CheckboxType;
|
||||
use crate::types::IndentationLevel;
|
||||
use crate::types::Keyword;
|
||||
use crate::types::Object;
|
||||
use crate::types::PlainList;
|
||||
use crate::types::PlainListItem;
|
||||
@@ -81,14 +82,17 @@ pub(crate) fn detect_plain_list<'b, 'g, 'r, 's>(
|
||||
|
||||
#[cfg_attr(
|
||||
feature = "tracing",
|
||||
tracing::instrument(ret, level = "debug", skip(context))
|
||||
tracing::instrument(ret, level = "debug", skip(context, affiliated_keywords))
|
||||
)]
|
||||
pub(crate) fn plain_list<'b, 'g, 'r, 's>(
|
||||
pub(crate) fn plain_list<'b, 'g, 'r, 's, AK>(
|
||||
affiliated_keywords: AK,
|
||||
remaining: OrgSource<'s>,
|
||||
context: RefContext<'b, 'g, 'r, 's>,
|
||||
input: OrgSource<'s>,
|
||||
) -> Res<OrgSource<'s>, PlainList<'s>> {
|
||||
let (remaining, affiliated_keywords) = many0(affiliated_keyword)(input)?;
|
||||
|
||||
) -> Res<OrgSource<'s>, PlainList<'s>>
|
||||
where
|
||||
AK: IntoIterator<Item = Keyword<'s>>,
|
||||
{
|
||||
let contexts = [
|
||||
ContextElement::Context("plain list"),
|
||||
ContextElement::ConsumeTrailingWhitespace(true),
|
||||
@@ -163,7 +167,10 @@ pub(crate) fn plain_list<'b, 'g, 'r, 's>(
|
||||
remaining,
|
||||
PlainList {
|
||||
source: source.into(),
|
||||
name: get_name(&affiliated_keywords),
|
||||
affiliated_keywords: parse_affiliated_keywords(
|
||||
context.get_global_settings(),
|
||||
affiliated_keywords,
|
||||
),
|
||||
list_type: first_item_list_type.expect("Plain lists require at least one element."),
|
||||
children: children.into_iter().map(|(_start, item)| item).collect(),
|
||||
},
|
||||
@@ -577,8 +584,8 @@ mod tests {
|
||||
let global_settings = GlobalSettings::default();
|
||||
let initial_context = ContextElement::document_context();
|
||||
let initial_context = Context::new(&global_settings, List::new(&initial_context));
|
||||
let plain_list_matcher = parser_with_context!(plain_list)(&initial_context);
|
||||
let (remaining, result) = plain_list_matcher(input).unwrap();
|
||||
let (remaining, result) =
|
||||
plain_list(std::iter::empty(), input, &initial_context, input).unwrap();
|
||||
assert_eq!(Into::<&str>::into(remaining), "");
|
||||
assert_eq!(result.get_standard_properties().get_source(), "1.");
|
||||
}
|
||||
@@ -589,8 +596,8 @@ mod tests {
|
||||
let global_settings = GlobalSettings::default();
|
||||
let initial_context = ContextElement::document_context();
|
||||
let initial_context = Context::new(&global_settings, List::new(&initial_context));
|
||||
let plain_list_matcher = parser_with_context!(plain_list)(&initial_context);
|
||||
let (remaining, result) = plain_list_matcher(input).unwrap();
|
||||
let (remaining, result) =
|
||||
plain_list(std::iter::empty(), input, &initial_context, input).unwrap();
|
||||
assert_eq!(Into::<&str>::into(remaining), "");
|
||||
assert_eq!(result.get_standard_properties().get_source(), "1. foo");
|
||||
}
|
||||
@@ -602,8 +609,7 @@ mod tests {
|
||||
let global_settings = GlobalSettings::default();
|
||||
let initial_context = ContextElement::document_context();
|
||||
let initial_context = Context::new(&global_settings, List::new(&initial_context));
|
||||
let plain_list_matcher = parser_with_context!(plain_list)(&initial_context);
|
||||
let result = plain_list_matcher(input);
|
||||
let result = plain_list(std::iter::empty(), input, &initial_context, input);
|
||||
assert!(result.is_err());
|
||||
}
|
||||
|
||||
@@ -614,8 +620,7 @@ mod tests {
|
||||
let global_settings = GlobalSettings::default();
|
||||
let initial_context = ContextElement::document_context();
|
||||
let initial_context = Context::new(&global_settings, List::new(&initial_context));
|
||||
let plain_list_matcher = parser_with_context!(plain_list)(&initial_context);
|
||||
let result = plain_list_matcher(input);
|
||||
let result = plain_list(std::iter::empty(), input, &initial_context, input);
|
||||
assert!(result.is_ok());
|
||||
}
|
||||
|
||||
|
||||
@@ -13,12 +13,12 @@ use nom::multi::many1;
|
||||
use nom::multi::many_till;
|
||||
use nom::sequence::tuple;
|
||||
|
||||
use super::affiliated_keyword::parse_affiliated_keywords;
|
||||
use super::keyword::affiliated_keyword;
|
||||
use super::keyword::table_formula_keyword;
|
||||
use super::object_parser::table_cell_set_object;
|
||||
use super::org_source::OrgSource;
|
||||
use super::util::exit_matcher_parser;
|
||||
use super::util::get_name;
|
||||
use super::util::maybe_consume_trailing_whitespace_if_not_exiting;
|
||||
use super::util::org_line_ending;
|
||||
use crate::context::parser_with_context;
|
||||
@@ -29,6 +29,7 @@ use crate::context::RefContext;
|
||||
use crate::error::Res;
|
||||
use crate::parser::util::get_consumed;
|
||||
use crate::parser::util::start_of_line;
|
||||
use crate::types::Keyword;
|
||||
use crate::types::Table;
|
||||
use crate::types::TableCell;
|
||||
use crate::types::TableRow;
|
||||
@@ -38,13 +39,17 @@ use crate::types::TableRow;
|
||||
/// This is not the table.el style.
|
||||
#[cfg_attr(
|
||||
feature = "tracing",
|
||||
tracing::instrument(ret, level = "debug", skip(context))
|
||||
tracing::instrument(ret, level = "debug", skip(context, affiliated_keywords))
|
||||
)]
|
||||
pub(crate) fn org_mode_table<'b, 'g, 'r, 's>(
|
||||
pub(crate) fn org_mode_table<'b, 'g, 'r, 's, AK>(
|
||||
affiliated_keywords: AK,
|
||||
remaining: OrgSource<'s>,
|
||||
context: RefContext<'b, 'g, 'r, 's>,
|
||||
input: OrgSource<'s>,
|
||||
) -> Res<OrgSource<'s>, Table<'s>> {
|
||||
let (remaining, affiliated_keywords) = many0(affiliated_keyword)(input)?;
|
||||
) -> Res<OrgSource<'s>, Table<'s>>
|
||||
where
|
||||
AK: IntoIterator<Item = Keyword<'s>>,
|
||||
{
|
||||
start_of_line(remaining)?;
|
||||
peek(tuple((space0, tag("|"))))(remaining)?;
|
||||
|
||||
@@ -77,7 +82,10 @@ pub(crate) fn org_mode_table<'b, 'g, 'r, 's>(
|
||||
remaining,
|
||||
Table {
|
||||
source: source.into(),
|
||||
name: get_name(&affiliated_keywords),
|
||||
affiliated_keywords: parse_affiliated_keywords(
|
||||
context.get_global_settings(),
|
||||
affiliated_keywords,
|
||||
),
|
||||
formulas,
|
||||
children,
|
||||
},
|
||||
|
||||
@@ -23,7 +23,6 @@ use crate::error::CustomError;
|
||||
use crate::error::MyError;
|
||||
use crate::error::Res;
|
||||
use crate::types::IndentationLevel;
|
||||
use crate::types::Keyword;
|
||||
|
||||
pub(crate) const WORD_CONSTITUENT_CHARACTERS: &str =
|
||||
"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
|
||||
@@ -272,39 +271,6 @@ pub(crate) fn indentation_level<'b, 'g, 'r, 's>(
|
||||
Ok((remaining, (indentation_level, leading_whitespace)))
|
||||
}
|
||||
|
||||
pub(crate) fn get_name<'s>(affiliated_keywords: &Vec<Keyword<'s>>) -> Option<&'s str> {
|
||||
let name_keyword = affiliated_keywords
|
||||
.iter()
|
||||
.filter(|kw| {
|
||||
kw.key.eq_ignore_ascii_case("name")
|
||||
|| kw.key.eq_ignore_ascii_case("source")
|
||||
|| kw.key.eq_ignore_ascii_case("tblname")
|
||||
|| kw.key.eq_ignore_ascii_case("resname")
|
||||
|| kw.key.eq_ignore_ascii_case("srcname")
|
||||
|| kw.key.eq_ignore_ascii_case("label")
|
||||
})
|
||||
.last();
|
||||
name_keyword.map(|kw| kw.value)
|
||||
}
|
||||
|
||||
pub(crate) fn get_has_affiliated_keyword<'b, 'g, 'r, 's>(
|
||||
context: RefContext<'b, 'g, 'r, 's>,
|
||||
) -> Option<OrgSource<'s>> {
|
||||
for context in context.iter() {
|
||||
match context {
|
||||
ContextElement::HasAffiliatedKeyword(inner) => {
|
||||
if !inner.keywords.is_empty() {
|
||||
return Some(inner.start_after_affiliated_keywords);
|
||||
} else {
|
||||
return None;
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
/// Reset the input OrgSource as if it was starting a fresh document.
|
||||
///
|
||||
/// This is important for making start-of-document, end-of-document, and other context-dependent tests succeed.
|
||||
|
||||
33
src/types/affiliated_keyword.rs
Normal file
33
src/types/affiliated_keyword.rs
Normal file
@@ -0,0 +1,33 @@
|
||||
use std::collections::BTreeMap;
|
||||
|
||||
use super::Object;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum AffiliatedKeywordValue<'s> {
|
||||
SingleString(&'s str),
|
||||
ListOfStrings(Vec<&'s str>),
|
||||
ListOfListsOfObjects(Vec<(Option<Vec<Object<'s>>>, Vec<Object<'s>>)>),
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct AffiliatedKeyword<'s> {
|
||||
pub name: &'s str,
|
||||
pub value: AffiliatedKeywordValue<'s>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct AffiliatedKeywords<'s> {
|
||||
pub(crate) keywords: BTreeMap<String, AffiliatedKeywordValue<'s>>,
|
||||
}
|
||||
|
||||
pub trait GetAffiliatedKeywords<'s> {
|
||||
fn get_affiliated_keywords<'a>(&'a self) -> &'a AffiliatedKeywords<'s>;
|
||||
}
|
||||
|
||||
impl<'s> Default for AffiliatedKeywords<'s> {
|
||||
fn default() -> Self {
|
||||
AffiliatedKeywords {
|
||||
keywords: BTreeMap::new(),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2,6 +2,7 @@ use std::path::PathBuf;
|
||||
|
||||
use super::Element;
|
||||
use super::GetStandardProperties;
|
||||
use super::NodeProperty;
|
||||
use super::Object;
|
||||
use super::StandardProperties;
|
||||
use super::Timestamp;
|
||||
@@ -90,4 +91,43 @@ impl<'s> Heading<'s> {
|
||||
.collect();
|
||||
title_source
|
||||
}
|
||||
|
||||
pub fn get_additional_properties(&self) -> impl Iterator<Item = &NodeProperty<'s>> {
|
||||
self.children
|
||||
.iter()
|
||||
.take(1)
|
||||
.filter_map(|c| match c {
|
||||
DocumentElement::Section(section) => Some(section),
|
||||
_ => None,
|
||||
})
|
||||
.flat_map(|section| section.children.iter())
|
||||
.take(1)
|
||||
.filter_map(|element| match element {
|
||||
Element::PropertyDrawer(property_drawer) => Some(property_drawer),
|
||||
_ => None,
|
||||
})
|
||||
.flat_map(|property_drawer| property_drawer.children.iter())
|
||||
}
|
||||
}
|
||||
|
||||
impl<'s> Document<'s> {
|
||||
pub fn get_additional_properties(&self) -> impl Iterator<Item = &NodeProperty<'s>> {
|
||||
let zeroth_section_children = self
|
||||
.zeroth_section
|
||||
.iter()
|
||||
.flat_map(|zeroth_section| zeroth_section.children.iter());
|
||||
let property_drawer = zeroth_section_children
|
||||
.take_while(|element| match element {
|
||||
Element::Comment(_) => true,
|
||||
Element::PropertyDrawer(_) => true,
|
||||
_ => false,
|
||||
})
|
||||
.find_map(|element| match element {
|
||||
Element::PropertyDrawer(property_drawer) => Some(property_drawer),
|
||||
_ => None,
|
||||
});
|
||||
property_drawer
|
||||
.into_iter()
|
||||
.flat_map(|property_drawer| property_drawer.children.iter())
|
||||
}
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user