Compare commits
1 Commits
v0.1.3
...
feature_ma
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
678106bb65 |
@@ -77,9 +77,19 @@ spec:
|
|||||||
workspace: docker-credentials
|
workspace: docker-credentials
|
||||||
runAfter:
|
runAfter:
|
||||||
- fetch-repository
|
- fetch-repository
|
||||||
- name: run-image-none
|
- name: build-organic
|
||||||
taskRef:
|
taskRef:
|
||||||
name: run-docker-image
|
name: run-docker-image
|
||||||
|
matrix:
|
||||||
|
params:
|
||||||
|
- name: feature-compare
|
||||||
|
value:
|
||||||
|
- "true"
|
||||||
|
- "false"
|
||||||
|
- name: feature-tracing
|
||||||
|
value:
|
||||||
|
- "true"
|
||||||
|
- "false"
|
||||||
workspaces:
|
workspaces:
|
||||||
- name: source
|
- name: source
|
||||||
workspace: git-source
|
workspace: git-source
|
||||||
@@ -88,68 +98,22 @@ spec:
|
|||||||
runAfter:
|
runAfter:
|
||||||
- build-image
|
- build-image
|
||||||
params:
|
params:
|
||||||
|
- name: command
|
||||||
|
value: ["/bin/sh", "-c"]
|
||||||
- name: args
|
- name: args
|
||||||
value: ["--no-default-features"]
|
value:
|
||||||
- name: docker-image
|
- |
|
||||||
value: "$(params.image-name):$(tasks.fetch-repository.results.commit)"
|
set -euo pipefail
|
||||||
- name: run-image-tracing
|
IFS=$$'\n\t'
|
||||||
taskRef:
|
features=()
|
||||||
name: run-docker-image
|
if [ $(params.feature-compare) = "true" ]; then features+=(compare); fi
|
||||||
workspaces:
|
if [ $(params.feature-tracing) = "true" ]; then features+=(tracing); fi
|
||||||
- name: source
|
if [ $${#features[@]} -eq 0 ]; then
|
||||||
workspace: git-source
|
exec cargo build --no-default-features
|
||||||
- name: cargo-cache
|
else
|
||||||
workspace: cargo-cache
|
featurelist=$$(IFS="," ; echo "$${features[*]}")
|
||||||
runAfter:
|
exec cargo build --no-default-features --features "$$featurelist"
|
||||||
- run-image-none
|
fi
|
||||||
params:
|
|
||||||
- name: args
|
|
||||||
value: ["--no-default-features", "--features", "tracing"]
|
|
||||||
- name: docker-image
|
|
||||||
value: "$(params.image-name):$(tasks.fetch-repository.results.commit)"
|
|
||||||
- name: run-image-compare
|
|
||||||
taskRef:
|
|
||||||
name: run-docker-image
|
|
||||||
workspaces:
|
|
||||||
- name: source
|
|
||||||
workspace: git-source
|
|
||||||
- name: cargo-cache
|
|
||||||
workspace: cargo-cache
|
|
||||||
runAfter:
|
|
||||||
- run-image-tracing
|
|
||||||
params:
|
|
||||||
- name: args
|
|
||||||
value: ["--no-default-features", "--features", "compare"]
|
|
||||||
- name: docker-image
|
|
||||||
value: "$(params.image-name):$(tasks.fetch-repository.results.commit)"
|
|
||||||
- name: run-image-default
|
|
||||||
taskRef:
|
|
||||||
name: run-docker-image
|
|
||||||
workspaces:
|
|
||||||
- name: source
|
|
||||||
workspace: git-source
|
|
||||||
- name: cargo-cache
|
|
||||||
workspace: cargo-cache
|
|
||||||
runAfter:
|
|
||||||
- run-image-compare
|
|
||||||
params:
|
|
||||||
- name: args
|
|
||||||
value: []
|
|
||||||
- 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-default
|
|
||||||
params:
|
|
||||||
- name: args
|
|
||||||
value: ["--no-default-features", "--features", "tracing,compare"]
|
|
||||||
- name: docker-image
|
- name: docker-image
|
||||||
value: "$(params.image-name):$(tasks.fetch-repository.results.commit)"
|
value: "$(params.image-name):$(tasks.fetch-repository.results.commit)"
|
||||||
finally:
|
finally:
|
||||||
|
|||||||
@@ -18,6 +18,14 @@ spec:
|
|||||||
- name: path-to-dockerfile
|
- name: path-to-dockerfile
|
||||||
description: The path to the Dockerfile
|
description: The path to the Dockerfile
|
||||||
type: string
|
type: string
|
||||||
|
- name: command
|
||||||
|
type: array
|
||||||
|
description: Command to run.
|
||||||
|
default: []
|
||||||
|
- name: args
|
||||||
|
type: array
|
||||||
|
description: Arguments passed to command.
|
||||||
|
default: []
|
||||||
tasks:
|
tasks:
|
||||||
- name: do-stuff
|
- name: do-stuff
|
||||||
taskSpec:
|
taskSpec:
|
||||||
@@ -109,8 +117,10 @@ spec:
|
|||||||
runAfter:
|
runAfter:
|
||||||
- build-image
|
- build-image
|
||||||
params:
|
params:
|
||||||
|
- name: command
|
||||||
|
value: ["$(params.command[*])"]
|
||||||
- name: args
|
- name: args
|
||||||
value: [--no-fail-fast, --lib, --test, test_loader]
|
value: ["$(params.args[*])"]
|
||||||
- name: docker-image
|
- name: docker-image
|
||||||
value: "$(params.image-name):$(tasks.fetch-repository.results.commit)"
|
value: "$(params.image-name):$(tasks.fetch-repository.results.commit)"
|
||||||
finally:
|
finally:
|
||||||
@@ -202,3 +212,7 @@ spec:
|
|||||||
value: docker/organic_test/
|
value: docker/organic_test/
|
||||||
- name: path-to-dockerfile
|
- name: path-to-dockerfile
|
||||||
value: docker/organic_test/Dockerfile
|
value: docker/organic_test/Dockerfile
|
||||||
|
- name: command
|
||||||
|
value: [cargo, test]
|
||||||
|
- name: args
|
||||||
|
value: [--lib, --test, test_loader]
|
||||||
|
|||||||
@@ -14,6 +14,14 @@ spec:
|
|||||||
- name: path-to-dockerfile
|
- name: path-to-dockerfile
|
||||||
description: The path to the Dockerfile
|
description: The path to the Dockerfile
|
||||||
type: string
|
type: string
|
||||||
|
- name: rustfmt-command
|
||||||
|
type: array
|
||||||
|
description: Command to run rustfmt.
|
||||||
|
default: []
|
||||||
|
- name: rustfmt-args
|
||||||
|
type: array
|
||||||
|
description: Arguments passed to rustfmt.
|
||||||
|
default: []
|
||||||
- name: GIT_USER_NAME
|
- name: GIT_USER_NAME
|
||||||
description: The username for git
|
description: The username for git
|
||||||
type: string
|
type: string
|
||||||
@@ -111,6 +119,10 @@ spec:
|
|||||||
runAfter:
|
runAfter:
|
||||||
- build-image
|
- build-image
|
||||||
params:
|
params:
|
||||||
|
- name: command
|
||||||
|
value: ["$(params.rustfmt-command[*])"]
|
||||||
|
- name: args
|
||||||
|
value: ["$(params.rustfmt-args[*])"]
|
||||||
- name: docker-image
|
- name: docker-image
|
||||||
value: "$(params.image-name):$(tasks.fetch-repository.results.commit)"
|
value: "$(params.image-name):$(tasks.fetch-repository.results.commit)"
|
||||||
- name: cargo-fix
|
- name: cargo-fix
|
||||||
@@ -228,3 +240,7 @@ spec:
|
|||||||
value: docker/cargo_fmt/
|
value: docker/cargo_fmt/
|
||||||
- name: path-to-dockerfile
|
- name: path-to-dockerfile
|
||||||
value: docker/cargo_fmt/Dockerfile
|
value: docker/cargo_fmt/Dockerfile
|
||||||
|
- name: command
|
||||||
|
value: [cargo, fmt]
|
||||||
|
- name: args
|
||||||
|
value: []
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "organic"
|
name = "organic"
|
||||||
version = "0.1.3"
|
version = "0.1.2"
|
||||||
authors = ["Tom Alexander <tom@fizz.buzz>"]
|
authors = ["Tom Alexander <tom@fizz.buzz>"]
|
||||||
description = "An org-mode parser."
|
description = "An org-mode parser."
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
@@ -14,8 +14,7 @@ include = [
|
|||||||
"LICENSE",
|
"LICENSE",
|
||||||
"**/*.rs",
|
"**/*.rs",
|
||||||
"Cargo.toml",
|
"Cargo.toml",
|
||||||
"tests/*",
|
"tests/*"
|
||||||
"org_mode_samples/"
|
|
||||||
]
|
]
|
||||||
|
|
||||||
[lib]
|
[lib]
|
||||||
@@ -41,17 +40,15 @@ tracing-subscriber = { version = "0.3.17", optional = true, features = ["env-fil
|
|||||||
walkdir = "2.3.3"
|
walkdir = "2.3.3"
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
default = []
|
default = ["compare"]
|
||||||
compare = []
|
compare = []
|
||||||
tracing = ["dep:opentelemetry", "dep:opentelemetry-otlp", "dep:opentelemetry-semantic-conventions", "dep:tokio", "dep:tracing", "dep:tracing-opentelemetry", "dep:tracing-subscriber"]
|
tracing = ["dep:opentelemetry", "dep:opentelemetry-otlp", "dep:opentelemetry-semantic-conventions", "dep:tokio", "dep:tracing", "dep:tracing-opentelemetry", "dep:tracing-subscriber"]
|
||||||
|
|
||||||
# Optimized build for any sort of release.
|
|
||||||
[profile.release-lto]
|
[profile.release-lto]
|
||||||
inherits = "release"
|
inherits = "release"
|
||||||
lto = true
|
lto = true
|
||||||
strip = "symbols"
|
strip = "symbols"
|
||||||
|
|
||||||
# Profile for performance testing with the "perf" tool. Notably keeps debug enabled and does not strip symbols to make reading the perf output easier.
|
|
||||||
[profile.perf]
|
[profile.perf]
|
||||||
inherits = "release"
|
inherits = "release"
|
||||||
lto = true
|
lto = true
|
||||||
|
|||||||
2
Makefile
2
Makefile
@@ -40,7 +40,7 @@ test:
|
|||||||
.PHONY: dockertest
|
.PHONY: dockertest
|
||||||
dockertest:
|
dockertest:
|
||||||
> $(MAKE) -C docker/organic_test
|
> $(MAKE) -C docker/organic_test
|
||||||
> docker run --init --rm -i -t -v "$$(readlink -f ./):/source:ro" --mount source=cargo-cache,target=/usr/local/cargo/registry --mount source=rust-cache,target=/target --env CARGO_TARGET_DIR=/target -w /source organic-test --no-fail-fast --lib --test test_loader -- --test-threads $(TESTJOBS)
|
> docker run --init --rm -i -t -v "$$(readlink -f ./):/source:ro" --mount source=cargo-cache,target=/usr/local/cargo/registry --mount source=rust-cache,target=/target --env CARGO_TARGET_DIR=/target -w /source organic-test cargo test --no-fail-fast --lib --test test_loader -- --test-threads $(TESTJOBS)
|
||||||
|
|
||||||
.PHONY: dockerclean
|
.PHONY: dockerclean
|
||||||
dockerclean:
|
dockerclean:
|
||||||
|
|||||||
1
build.rs
1
build.rs
@@ -75,7 +75,6 @@ fn is_expect_fail(name: &str) -> Option<&str> {
|
|||||||
"autogen_greater_element_drawer_drawer_with_headline_inside" => Some("Apparently lines with :end: become their own paragraph. This odd behavior needs to be investigated more."),
|
"autogen_greater_element_drawer_drawer_with_headline_inside" => Some("Apparently lines with :end: become their own paragraph. This odd behavior needs to be investigated more."),
|
||||||
"autogen_element_container_priority_footnote_definition_dynamic_block" => Some("Apparently broken begin lines become their own paragraph."),
|
"autogen_element_container_priority_footnote_definition_dynamic_block" => Some("Apparently broken begin lines become their own paragraph."),
|
||||||
"autogen_lesser_element_paragraphs_paragraph_with_backslash_line_breaks" => Some("The text we're getting out of the parse tree is already processed to remove line breaks, so our comparison needs to take that into account."),
|
"autogen_lesser_element_paragraphs_paragraph_with_backslash_line_breaks" => Some("The text we're getting out of the parse tree is already processed to remove line breaks, so our comparison needs to take that into account."),
|
||||||
"autogen_unicode_hearts" => Some("Unicode is coming out of emacs strange."),
|
|
||||||
_ => None,
|
_ => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -26,11 +26,10 @@ else
|
|||||||
@echo "REMOTE_REPO not defined, not removing from remote repo."
|
@echo "REMOTE_REPO not defined, not removing from remote repo."
|
||||||
endif
|
endif
|
||||||
|
|
||||||
# NOTE: This target will write to folders underneath the git-root
|
|
||||||
.PHONY: run
|
.PHONY: run
|
||||||
run: build
|
run:
|
||||||
docker run --rm --init -v "$$(readlink -f ../../):/source" --workdir=/source $(IMAGE_NAME)
|
docker run --rm -i -t $(IMAGE_NAME)
|
||||||
|
|
||||||
.PHONY: shell
|
.PHONY: shell
|
||||||
shell: build
|
shell:
|
||||||
docker run --rm -i -t --entrypoint /bin/sh -v "$$(readlink -f ../../):/source" --workdir=/source $(IMAGE_NAME)
|
docker run --rm -i -t --entrypoint /bin/bash $(IMAGE_NAME)
|
||||||
|
|||||||
@@ -2,5 +2,3 @@ FROM rustlang/rust:nightly-alpine3.17
|
|||||||
|
|
||||||
RUN apk add --no-cache musl-dev
|
RUN apk add --no-cache musl-dev
|
||||||
RUN cargo install --locked --no-default-features --features ci-autoclean cargo-cache
|
RUN cargo install --locked --no-default-features --features ci-autoclean cargo-cache
|
||||||
|
|
||||||
ENTRYPOINT ["cargo", "build"]
|
|
||||||
|
|||||||
@@ -25,13 +25,11 @@ ifdef REMOTE_REPO
|
|||||||
else
|
else
|
||||||
@echo "REMOTE_REPO not defined, not removing from remote repo."
|
@echo "REMOTE_REPO not defined, not removing from remote repo."
|
||||||
endif
|
endif
|
||||||
docker volume rm cargo-cache
|
|
||||||
|
|
||||||
# NOTE: This target will write to folders underneath the git-root
|
|
||||||
.PHONY: run
|
.PHONY: run
|
||||||
run: build
|
run:
|
||||||
docker run --rm --init -v "$$(readlink -f ../../):/source" --workdir=/source --mount source=cargo-cache,target=/usr/local/cargo/registry $(IMAGE_NAME)
|
docker run --rm -i -t $(IMAGE_NAME)
|
||||||
|
|
||||||
.PHONY: shell
|
.PHONY: shell
|
||||||
shell: build
|
shell:
|
||||||
docker run --rm -i -t --entrypoint /bin/sh -v "$$(readlink -f ../../):/source" --workdir=/source --mount source=cargo-cache,target=/usr/local/cargo/registry $(IMAGE_NAME)
|
docker run --rm -i -t --entrypoint /bin/bash $(IMAGE_NAME)
|
||||||
|
|||||||
@@ -30,5 +30,3 @@ RUN apk add --no-cache musl-dev ncurses gnutls
|
|||||||
RUN cargo install --locked --no-default-features --features ci-autoclean cargo-cache
|
RUN cargo install --locked --no-default-features --features ci-autoclean cargo-cache
|
||||||
COPY --from=build-emacs /root/dist/ /
|
COPY --from=build-emacs /root/dist/ /
|
||||||
COPY --from=build-org-mode /root/dist/ /
|
COPY --from=build-org-mode /root/dist/ /
|
||||||
|
|
||||||
ENTRYPOINT ["cargo", "test"]
|
|
||||||
|
|||||||
@@ -25,12 +25,11 @@ ifdef REMOTE_REPO
|
|||||||
else
|
else
|
||||||
@echo "REMOTE_REPO not defined, not removing from remote repo."
|
@echo "REMOTE_REPO not defined, not removing from remote repo."
|
||||||
endif
|
endif
|
||||||
docker volume rm rust-cache cargo-cache
|
|
||||||
|
|
||||||
.PHONY: run
|
.PHONY: run
|
||||||
run: build
|
run:
|
||||||
docker run --rm --init -v "$$(readlink -f ../../):/source:ro" --workdir=/source --mount source=cargo-cache,target=/usr/local/cargo/registry --mount source=rust-cache,target=/target --env CARGO_TARGET_DIR=/target $(IMAGE_NAME) --no-fail-fast --lib --test test_loader
|
docker run --rm -i -t $(IMAGE_NAME)
|
||||||
|
|
||||||
.PHONY: shell
|
.PHONY: shell
|
||||||
shell: build
|
shell:
|
||||||
docker run --rm -i -t --entrypoint /bin/sh -v "$$(readlink -f ../../):/source:ro" --workdir=/source --mount source=cargo-cache,target=/usr/local/cargo/registry --mount source=rust-cache,target=/target --env CARGO_TARGET_DIR=/target $(IMAGE_NAME)
|
docker run --rm -i -t --entrypoint /bin/bash $(IMAGE_NAME)
|
||||||
|
|||||||
@@ -1 +0,0 @@
|
|||||||
This folder is for snippets of elisp that are useful for development.
|
|
||||||
@@ -1,3 +0,0 @@
|
|||||||
(dolist (var org-element-affiliated-keywords)
|
|
||||||
(message "\"%s\"," (downcase var))
|
|
||||||
)
|
|
||||||
@@ -1,5 +0,0 @@
|
|||||||
(dolist (var org-entities)
|
|
||||||
(when (listp var)
|
|
||||||
(message "\"%s\"," (nth 0 var))
|
|
||||||
)
|
|
||||||
)
|
|
||||||
@@ -1,7 +1,6 @@
|
|||||||
1. plain-list
|
1. foo
|
||||||
#+begin_center
|
#+begin_center
|
||||||
|
|
||||||
|
|
||||||
#+end_center
|
#+end_center
|
||||||
|
2. bar
|
||||||
Is this still in the plain list?
|
|
||||||
|
|||||||
1
org_mode_samples/exit_matcher_investigation/README.txt
Normal file
1
org_mode_samples/exit_matcher_investigation/README.txt
Normal file
@@ -0,0 +1 @@
|
|||||||
|
This folder is an investigation into whether or not my exit matchers should operate from the top down or bottom up.
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
foo *bar baz * lorem* ipsum
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
Looks like 2 blank lines always exits the top-level plain list.
|
||||||
|
|
||||||
|
Plain lists do not seem to go inside paragraphs but rather exist beside them.
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
1. foo
|
||||||
|
|
||||||
|
bar
|
||||||
|
|
||||||
|
1. baz
|
||||||
|
|
||||||
|
lorem
|
||||||
|
|
||||||
|
ipsum
|
||||||
|
|
||||||
|
|
||||||
|
dolar
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
Looks like table cells cannot contain lists but can contain bolds
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
ip *su* m
|
||||||
|
|
||||||
|
| foo | bar |
|
||||||
|
|----------+-----|
|
||||||
|
| 1. lo *re* m | |
|
||||||
@@ -1,25 +0,0 @@
|
|||||||
#+BEGIN: clocktable :scope file :maxlevel 2
|
|
||||||
#+CAPTION: Clock summary at [2023-08-25 Fri 05:34]
|
|
||||||
| Headline | Time |
|
|
||||||
|--------------+--------|
|
|
||||||
| *Total time* | *0:00* |
|
|
||||||
#+END:
|
|
||||||
|
|
||||||
#+BEGIN: columnview :hlines 1 :id global
|
|
||||||
| ITEM | TODO | PRIORITY | TAGS |
|
|
||||||
|-------+------+----------+------------------------------|
|
|
||||||
| Foo | | B | |
|
|
||||||
|-------+------+----------+------------------------------|
|
|
||||||
| Bar | TODO | B | |
|
|
||||||
|-------+------+----------+------------------------------|
|
|
||||||
| Baz | | B | :thisisatag: |
|
|
||||||
| Lorem | | B | :thisshouldinheritfromabove: |
|
|
||||||
| Ipsum | | B | :multiple:tags: |
|
|
||||||
#+END:
|
|
||||||
* Foo
|
|
||||||
* TODO Bar
|
|
||||||
* Baz :thisisatag:
|
|
||||||
** Lorem :thisshouldinheritfromabove:
|
|
||||||
*** Ipsum :multiple:tags:
|
|
||||||
* Dolar ::
|
|
||||||
* cat :dog: bat
|
|
||||||
@@ -1,7 +0,0 @@
|
|||||||
1. foo
|
|
||||||
2.
|
|
||||||
bar
|
|
||||||
1.
|
|
||||||
#+begin_center
|
|
||||||
Still in the list
|
|
||||||
#+end_center
|
|
||||||
@@ -1,2 +0,0 @@
|
|||||||
- foo ::
|
|
||||||
|
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
- foo :: bar
|
|
||||||
- cat ::
|
|
||||||
dog
|
|
||||||
- lorem
|
|
||||||
:: ipsum
|
|
||||||
-
|
|
||||||
lorem :: ipsum
|
|
||||||
- dolar *bold* foo :: ipsum
|
|
||||||
- big gap ::
|
|
||||||
|
|
||||||
stuff
|
|
||||||
@@ -1,7 +0,0 @@
|
|||||||
** foo
|
|
||||||
:PROPERTIES:
|
|
||||||
:DESCRIPTION: lorem
|
|
||||||
:ALT_TITLE: ipsum
|
|
||||||
:END:
|
|
||||||
|
|
||||||
bar
|
|
||||||
@@ -1,15 +0,0 @@
|
|||||||
#+name: foo
|
|
||||||
#+caption: bar
|
|
||||||
#+caption: baz
|
|
||||||
|
|
||||||
[[file:lorem/ipsum.png]]
|
|
||||||
|
|
||||||
#+name: cat
|
|
||||||
#+foo: dog
|
|
||||||
[[file:lorem/ipsum.png]]
|
|
||||||
|
|
||||||
#+name: cat
|
|
||||||
#+foo: dog
|
|
||||||
|
|
||||||
|
|
||||||
foo
|
|
||||||
@@ -1,22 +0,0 @@
|
|||||||
# Extra open
|
|
||||||
[cite/a/b-_/foo:unbalancedglobal[prefix;keyprefix @foo keysuffix;globalsuffix]
|
|
||||||
|
|
||||||
[cite/a/b-_/foo:globalprefix;unbalancedkey[prefix @foo keysuffix;globalsuffix]
|
|
||||||
|
|
||||||
[cite/a/b-_/foo:globalprefix;keyprefix @foo unbalancedkey[suffix;globalsuffix]
|
|
||||||
|
|
||||||
[cite/a/b-_/foo:globalprefix;keyprefix @foo keysuffix;unbalancedglobal[suffix]
|
|
||||||
|
|
||||||
|
|
||||||
# Extra close
|
|
||||||
[cite/a/b-_/foo:unbalancedglobal]prefix;keyprefix @foo keysuffix;globalsuffix]
|
|
||||||
|
|
||||||
[cite/a/b-_/foo:globalprefix;unbalancedkey]prefix @foo keysuffix;globalsuffix]
|
|
||||||
|
|
||||||
[cite/a/b-_/foo:globalprefix;keyprefix @foo unbalancedkey]suffix;globalsuffix]
|
|
||||||
|
|
||||||
[cite/a/b-_/foo:globalprefix;keyprefix @foo keysuffix;unbalancedglobal]suffix]
|
|
||||||
|
|
||||||
|
|
||||||
# balanced:
|
|
||||||
[cite/a/b-_/foo:gl[obalpref]ix;ke[ypref]ix @foo ke[ysuff]ix;gl[obalsuff]ix]
|
|
||||||
@@ -1,2 +0,0 @@
|
|||||||
[fn:2:This footnote [ has balanced ] brackets inside it]
|
|
||||||
[fn::This footnote does not have balanced [ brackets inside it]
|
|
||||||
@@ -1,6 +0,0 @@
|
|||||||
$foo
|
|
||||||
bar
|
|
||||||
baz
|
|
||||||
lorem
|
|
||||||
ipsum
|
|
||||||
dolar$
|
|
||||||
@@ -1,52 +0,0 @@
|
|||||||
non-link text
|
|
||||||
eww://
|
|
||||||
rmail://
|
|
||||||
mhe://
|
|
||||||
irc://
|
|
||||||
info://
|
|
||||||
gnus://
|
|
||||||
docview://
|
|
||||||
bibtex://
|
|
||||||
bbdb://
|
|
||||||
w3m://
|
|
||||||
doi://
|
|
||||||
file+sys://
|
|
||||||
file+emacs://
|
|
||||||
shell://
|
|
||||||
news://
|
|
||||||
mailto://
|
|
||||||
https://
|
|
||||||
http://
|
|
||||||
ftp://
|
|
||||||
help://
|
|
||||||
file://
|
|
||||||
elisp://
|
|
||||||
randomfakeprotocl://
|
|
||||||
non-link text
|
|
||||||
|
|
||||||
|
|
||||||
non-link text
|
|
||||||
eww:
|
|
||||||
rmail:
|
|
||||||
mhe:
|
|
||||||
irc:
|
|
||||||
info:
|
|
||||||
gnus:
|
|
||||||
docview:
|
|
||||||
bibtex:
|
|
||||||
bbdb:
|
|
||||||
w3m:
|
|
||||||
doi:
|
|
||||||
file+sys:
|
|
||||||
file+emacs:
|
|
||||||
shell:
|
|
||||||
news:
|
|
||||||
mailto:
|
|
||||||
https:
|
|
||||||
http:
|
|
||||||
ftp:
|
|
||||||
help:
|
|
||||||
file:
|
|
||||||
elisp:
|
|
||||||
randomfakeprotocl:
|
|
||||||
non-link text
|
|
||||||
@@ -1,3 +0,0 @@
|
|||||||
mailto:foo@bar.baz.
|
|
||||||
|
|
||||||
mailto:foo@bar.baz....
|
|
||||||
@@ -1,17 +0,0 @@
|
|||||||
foo *bar
|
|
||||||
baz* lorem
|
|
||||||
|
|
||||||
text *markup
|
|
||||||
can
|
|
||||||
span* more
|
|
||||||
|
|
||||||
than *three
|
|
||||||
lines.
|
|
||||||
foo
|
|
||||||
bar* baz
|
|
||||||
|
|
||||||
foo *bar \\
|
|
||||||
baz \\
|
|
||||||
lorem \\
|
|
||||||
ipsum \\
|
|
||||||
dolar* cat
|
|
||||||
@@ -1,9 +0,0 @@
|
|||||||
* Foo
|
|
||||||
|
|
||||||
* Bar
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
* Baz
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
🧡💛💚💙💜
|
|
||||||
@@ -8,22 +8,11 @@ DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
|
|||||||
|
|
||||||
cd "$DIR/../"
|
cd "$DIR/../"
|
||||||
|
|
||||||
function main {
|
cargo build --profile "$PROFILE" --no-default-features
|
||||||
local additional_flags=()
|
perf record --freq=2000 --call-graph dwarf --output=perf.data target/${PROFILE}/compare
|
||||||
if [ "$PROFILE" = "dev" ] || [ "$PROFILE" = "debug" ]; then
|
# Convert to a format firefox will read
|
||||||
PROFILE="debug"
|
# flags to consider --show-info
|
||||||
else
|
perf script -F +pid --input perf.data > perf.firefox
|
||||||
additional_flags+=(--profile "$PROFILE")
|
|
||||||
fi
|
|
||||||
cargo build --no-default-features "${additional_flags[@]}"
|
|
||||||
perf record --freq=2000 --call-graph dwarf --output=perf.data target/${PROFILE}/compare
|
|
||||||
|
|
||||||
# Convert to a format firefox will read
|
echo "You probably want to go to https://profiler.firefox.com/"
|
||||||
# flags to consider --show-info
|
echo "Either that or run hotspot"
|
||||||
perf script -F +pid --input perf.data > perf.firefox
|
|
||||||
|
|
||||||
echo "You probably want to go to https://profiler.firefox.com/"
|
|
||||||
echo "Either that or run hotspot"
|
|
||||||
}
|
|
||||||
|
|
||||||
main "${@}"
|
|
||||||
|
|||||||
@@ -7,8 +7,6 @@ DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
|
|||||||
: ${SHELL:="NO"} # or YES to launch a shell instead of running the test
|
: ${SHELL:="NO"} # or YES to launch a shell instead of running the test
|
||||||
: ${TRACE:="NO"} # or YES to send traces to jaeger
|
: ${TRACE:="NO"} # or YES to send traces to jaeger
|
||||||
: ${BACKTRACE:="NO"} # or YES to print a rust backtrace when panicking
|
: ${BACKTRACE:="NO"} # or YES to print a rust backtrace when panicking
|
||||||
: ${NO_COLOR:=""} # Set to anything to disable color output
|
|
||||||
|
|
||||||
|
|
||||||
cd "$DIR/../"
|
cd "$DIR/../"
|
||||||
REALPATH=$(command -v uu-realpath || command -v realpath)
|
REALPATH=$(command -v uu-realpath || command -v realpath)
|
||||||
@@ -26,31 +24,23 @@ function build_container {
|
|||||||
function launch_container {
|
function launch_container {
|
||||||
local additional_flags=()
|
local additional_flags=()
|
||||||
local additional_args=()
|
local additional_args=()
|
||||||
local features=(compare)
|
|
||||||
|
|
||||||
if [ "$NO_COLOR" != "" ]; then
|
if [ "$SHELL" != "YES" ]; then
|
||||||
additional_flags+=(--env "NO_COLOR=$NO_COLOR")
|
additional_args+=(cargo run)
|
||||||
|
else
|
||||||
|
additional_flags+=(-t)
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [ "$TRACE" = "YES" ]; then
|
if [ "$TRACE" = "YES" ]; then
|
||||||
# We use the host network so it can talk to jaeger hosted at 127.0.0.1
|
# We use the host network so it can talk to jaeger hosted at 127.0.0.1
|
||||||
additional_flags+=(--network=host --env RUST_LOG=debug)
|
additional_flags+=(--network=host --env RUST_LOG=debug)
|
||||||
features+=(tracing)
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [ "$SHELL" != "YES" ]; then
|
|
||||||
local features_joined=$(IFS=","; echo "${features[*]}")
|
|
||||||
additional_args+=(cargo run --no-default-features --features "$features_joined")
|
|
||||||
else
|
|
||||||
additional_args+=(/bin/sh)
|
|
||||||
additional_flags+=(-t)
|
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [ "$BACKTRACE" = "YES" ]; then
|
if [ "$BACKTRACE" = "YES" ]; then
|
||||||
additional_flags+=(--env RUST_BACKTRACE=full)
|
additional_flags+=(--env RUST_BACKTRACE=full)
|
||||||
fi
|
fi
|
||||||
|
|
||||||
docker run "${additional_flags[@]}" --init --rm -i -v "$($REALPATH ./):/source:ro" --mount source=cargo-cache,target=/usr/local/cargo/registry --mount source=rust-cache,target=/target --env CARGO_TARGET_DIR=/target -w /source --entrypoint "" organic-test "${additional_args[@]}"
|
docker run "${additional_flags[@]}" --init --rm -i -v "$($REALPATH ./):/source:ro" --mount source=cargo-cache,target=/usr/local/cargo/registry --mount source=rust-cache,target=/target --env CARGO_TARGET_DIR=/target -w /source organic-test "${additional_args[@]}"
|
||||||
}
|
}
|
||||||
|
|
||||||
main "${@}"
|
main "${@}"
|
||||||
|
|||||||
@@ -4,8 +4,6 @@ set -euo pipefail
|
|||||||
IFS=$'\n\t'
|
IFS=$'\n\t'
|
||||||
DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
|
DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
|
||||||
|
|
||||||
: ${NO_COLOR:=""} # Set to anything to disable color output
|
|
||||||
|
|
||||||
cd "$DIR/../"
|
cd "$DIR/../"
|
||||||
REALPATH=$(command -v uu-realpath || command -v realpath)
|
REALPATH=$(command -v uu-realpath || command -v realpath)
|
||||||
MAKE=$(command -v gmake || command -v make)
|
MAKE=$(command -v gmake || command -v make)
|
||||||
@@ -42,11 +40,7 @@ function get_test_names {
|
|||||||
|
|
||||||
function launch_container {
|
function launch_container {
|
||||||
local test="$1"
|
local test="$1"
|
||||||
local additional_flags=()
|
local additional_args=()
|
||||||
|
|
||||||
if [ "$NO_COLOR" != "" ]; then
|
|
||||||
additional_flags+=(--env "NO_COLOR=$NO_COLOR")
|
|
||||||
fi
|
|
||||||
|
|
||||||
local init_script=$(cat <<EOF
|
local init_script=$(cat <<EOF
|
||||||
set -euo pipefail
|
set -euo pipefail
|
||||||
@@ -56,7 +50,7 @@ cargo test --no-fail-fast --lib --test test_loader "$test" -- --show-output
|
|||||||
EOF
|
EOF
|
||||||
)
|
)
|
||||||
|
|
||||||
docker run "${additional_flags[@]}" --init --rm -v "$($REALPATH ./):/source:ro" --mount source=cargo-cache,target=/usr/local/cargo/registry --mount source=rust-cache,target=/target --env CARGO_TARGET_DIR=/target -w /source --entrypoint "" organic-test sh -c "$init_script"
|
docker run --init --rm -v "$($REALPATH ./):/source:ro" --mount source=cargo-cache,target=/usr/local/cargo/registry --mount source=rust-cache,target=/target --env CARGO_TARGET_DIR=/target -w /source organic-test sh -c "$init_script"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,23 +0,0 @@
|
|||||||
#!/usr/bin/env bash
|
|
||||||
#
|
|
||||||
# Time running a single parse without invoking a compare with emacs.
|
|
||||||
set -euo pipefail
|
|
||||||
IFS=$'\n\t'
|
|
||||||
DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
|
|
||||||
|
|
||||||
: ${PROFILE:="release-lto"}
|
|
||||||
|
|
||||||
cd "$DIR/../"
|
|
||||||
|
|
||||||
function main {
|
|
||||||
local additional_flags=()
|
|
||||||
if [ "$PROFILE" = "dev" ] || [ "$PROFILE" = "debug" ]; then
|
|
||||||
PROFILE="debug"
|
|
||||||
else
|
|
||||||
additional_flags+=(--profile "$PROFILE")
|
|
||||||
fi
|
|
||||||
cargo build --no-default-features "${additional_flags[@]}"
|
|
||||||
time ./target/${PROFILE}/compare
|
|
||||||
}
|
|
||||||
|
|
||||||
main "${@}"
|
|
||||||
1440
src/compare/diff.rs
1440
src/compare/diff.rs
File diff suppressed because it is too large
Load Diff
@@ -47,11 +47,13 @@ pub fn assert_bounds<'s, S: Source<'s>>(
|
|||||||
standard_properties
|
standard_properties
|
||||||
.begin
|
.begin
|
||||||
.ok_or("Token should have a begin.")?,
|
.ok_or("Token should have a begin.")?,
|
||||||
standard_properties.end.ok_or("Token should have an end.")?,
|
standard_properties
|
||||||
|
.end
|
||||||
|
.ok_or("Token should have a begin.")?,
|
||||||
);
|
);
|
||||||
let (rust_begin, rust_end) = get_offsets(source, rust);
|
let (rust_begin, rust_end) = get_offsets(source, rust);
|
||||||
if (rust_begin + 1) != begin || (rust_end + 1) != end {
|
if (rust_begin + 1) != begin || (rust_end + 1) != end {
|
||||||
Err(format!("Rust bounds (in bytes) ({rust_begin}, {rust_end}) do not match emacs bounds ({emacs_begin}, {emacs_end})", rust_begin = rust_begin + 1, rust_end = rust_end + 1, emacs_begin=begin, emacs_end=end))?;
|
Err(format!("Rust bounds ({rust_begin}, {rust_end}) do not match emacs bounds ({emacs_begin}, {emacs_end})", rust_begin = rust_begin + 1, rust_end = rust_end + 1, emacs_begin=begin, emacs_end=end))?;
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
@@ -137,23 +139,3 @@ fn maybe_token_to_usize(
|
|||||||
.flatten() // Outer option is whether or not the param exists, inner option is whether or not it is nil
|
.flatten() // Outer option is whether or not the param exists, inner option is whether or not it is nil
|
||||||
.map_or(Ok(None), |r| r.map(Some))?)
|
.map_or(Ok(None), |r| r.map(Some))?)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_property<'s, 'x>(
|
|
||||||
emacs: &'s Token<'s>,
|
|
||||||
key: &'x str,
|
|
||||||
) -> Result<Option<&'s Token<'s>>, Box<dyn std::error::Error>> {
|
|
||||||
let children = emacs.as_list()?;
|
|
||||||
let attributes_child = children
|
|
||||||
.iter()
|
|
||||||
.nth(1)
|
|
||||||
.ok_or("Should have an attributes child.")?;
|
|
||||||
let attributes_map = attributes_child.as_map()?;
|
|
||||||
let prop = attributes_map
|
|
||||||
.get(key)
|
|
||||||
.ok_or(format!("Missing {} attribute.", key))?;
|
|
||||||
match prop.as_atom() {
|
|
||||||
Ok("nil") => return Ok(None),
|
|
||||||
_ => {}
|
|
||||||
};
|
|
||||||
Ok(Some(*prop))
|
|
||||||
}
|
|
||||||
|
|||||||
11
src/main.rs
11
src/main.rs
@@ -55,21 +55,20 @@ fn read_stdin_to_string() -> Result<String, Box<dyn std::error::Error>> {
|
|||||||
fn run_compare<P: AsRef<str>>(org_contents: P) -> Result<(), Box<dyn std::error::Error>> {
|
fn run_compare<P: AsRef<str>>(org_contents: P) -> Result<(), Box<dyn std::error::Error>> {
|
||||||
let emacs_version = get_emacs_version()?;
|
let emacs_version = get_emacs_version()?;
|
||||||
let org_mode_version = get_org_mode_version()?;
|
let org_mode_version = get_org_mode_version()?;
|
||||||
let org_contents = org_contents.as_ref();
|
|
||||||
eprintln!("Using emacs version: {}", emacs_version.trim());
|
eprintln!("Using emacs version: {}", emacs_version.trim());
|
||||||
eprintln!("Using org-mode version: {}", org_mode_version.trim());
|
eprintln!("Using org-mode version: {}", org_mode_version.trim());
|
||||||
let (remaining, rust_parsed) = document(org_contents).map_err(|e| e.to_string())?;
|
let (remaining, rust_parsed) = document(org_contents.as_ref()).map_err(|e| e.to_string())?;
|
||||||
let org_sexp = emacs_parse_org_document(org_contents)?;
|
let org_sexp = emacs_parse_org_document(org_contents.as_ref())?;
|
||||||
let (_remaining, parsed_sexp) =
|
let (_remaining, parsed_sexp) =
|
||||||
sexp_with_padding(org_sexp.as_str()).map_err(|e| e.to_string())?;
|
sexp_with_padding(org_sexp.as_str()).map_err(|e| e.to_string())?;
|
||||||
|
|
||||||
println!("{}\n\n\n", org_contents);
|
println!("{}\n\n\n", org_contents.as_ref());
|
||||||
println!("{}", org_sexp);
|
println!("{}", org_sexp);
|
||||||
println!("{:#?}", rust_parsed);
|
println!("{:#?}", rust_parsed);
|
||||||
|
|
||||||
// We do the diffing after printing out both parsed forms in case the diffing panics
|
// We do the diffing after printing out both parsed forms in case the diffing panics
|
||||||
let diff_result = compare_document(&parsed_sexp, &rust_parsed)?;
|
let diff_result = compare_document(&parsed_sexp, &rust_parsed)?;
|
||||||
diff_result.print(org_contents)?;
|
diff_result.print()?;
|
||||||
|
|
||||||
if diff_result.is_bad() {
|
if diff_result.is_bad() {
|
||||||
Err("Diff results do not match.")?;
|
Err("Diff results do not match.")?;
|
||||||
@@ -86,7 +85,7 @@ fn run_compare<P: AsRef<str>>(org_contents: P) -> Result<(), Box<dyn std::error:
|
|||||||
eprintln!(
|
eprintln!(
|
||||||
"This program was built with compare disabled. Only parsing with organic, not comparing."
|
"This program was built with compare disabled. Only parsing with organic, not comparing."
|
||||||
);
|
);
|
||||||
let (_remaining, rust_parsed) = document(org_contents.as_ref()).map_err(|e| e.to_string())?;
|
let (remaining, rust_parsed) = document(org_contents.as_ref()).map_err(|e| e.to_string())?;
|
||||||
println!("{:#?}", rust_parsed);
|
println!("{:#?}", rust_parsed);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,17 +11,17 @@ use nom::multi::many_till;
|
|||||||
use nom::multi::separated_list1;
|
use nom::multi::separated_list1;
|
||||||
use nom::sequence::tuple;
|
use nom::sequence::tuple;
|
||||||
|
|
||||||
use super::citation_reference::must_balance_bracket;
|
|
||||||
use super::org_source::BracketDepth;
|
|
||||||
use super::org_source::OrgSource;
|
use super::org_source::OrgSource;
|
||||||
use super::Context;
|
use super::Context;
|
||||||
use crate::error::CustomError;
|
use crate::error::CustomError;
|
||||||
use crate::error::Res;
|
use crate::error::Res;
|
||||||
use crate::parser::citation_reference::citation_reference;
|
use crate::parser::citation_reference::citation_reference;
|
||||||
use crate::parser::citation_reference::citation_reference_key;
|
use crate::parser::citation_reference::citation_reference_key;
|
||||||
|
use crate::parser::citation_reference::get_bracket_depth;
|
||||||
use crate::parser::exiting::ExitClass;
|
use crate::parser::exiting::ExitClass;
|
||||||
use crate::parser::object::Citation;
|
use crate::parser::object::Citation;
|
||||||
use crate::parser::object_parser::standard_set_object;
|
use crate::parser::object_parser::standard_set_object;
|
||||||
|
use crate::parser::parser_context::CitationBracket;
|
||||||
use crate::parser::parser_context::ContextElement;
|
use crate::parser::parser_context::ContextElement;
|
||||||
use crate::parser::parser_context::ExitMatcherNode;
|
use crate::parser::parser_context::ExitMatcherNode;
|
||||||
use crate::parser::parser_with_context::parser_with_context;
|
use crate::parser::parser_with_context::parser_with_context;
|
||||||
@@ -38,15 +38,13 @@ pub fn citation<'r, 's>(
|
|||||||
let (remaining, _) = tag_no_case("[cite")(input)?;
|
let (remaining, _) = tag_no_case("[cite")(input)?;
|
||||||
let (remaining, _) = opt(citestyle)(remaining)?;
|
let (remaining, _) = opt(citestyle)(remaining)?;
|
||||||
let (remaining, _) = tag(":")(remaining)?;
|
let (remaining, _) = tag(":")(remaining)?;
|
||||||
let (remaining, _prefix) =
|
let (remaining, _prefix) = opt(parser_with_context!(global_prefix)(context))(remaining)?;
|
||||||
must_balance_bracket(opt(parser_with_context!(global_prefix)(context)))(remaining)?;
|
|
||||||
|
|
||||||
let (remaining, _references) =
|
let (remaining, _references) =
|
||||||
separated_list1(tag(";"), parser_with_context!(citation_reference)(context))(remaining)?;
|
separated_list1(tag(";"), parser_with_context!(citation_reference)(context))(remaining)?;
|
||||||
let (remaining, _suffix) = must_balance_bracket(opt(tuple((
|
let (remaining, _suffix) = opt(tuple((
|
||||||
tag(";"),
|
tag(";"),
|
||||||
parser_with_context!(global_suffix)(context),
|
parser_with_context!(global_suffix)(context),
|
||||||
))))(remaining)?;
|
)))(remaining)?;
|
||||||
let (remaining, _) = tag("]")(remaining)?;
|
let (remaining, _) = tag("]")(remaining)?;
|
||||||
let (remaining, _) = space0(remaining)?;
|
let (remaining, _) = space0(remaining)?;
|
||||||
let source = get_consumed(input, remaining);
|
let source = get_consumed(input, remaining);
|
||||||
@@ -85,11 +83,15 @@ fn global_prefix<'r, 's>(
|
|||||||
context: Context<'r, 's>,
|
context: Context<'r, 's>,
|
||||||
input: OrgSource<'s>,
|
input: OrgSource<'s>,
|
||||||
) -> Res<OrgSource<'s>, Vec<Object<'s>>> {
|
) -> Res<OrgSource<'s>, Vec<Object<'s>>> {
|
||||||
let exit_with_depth = global_prefix_end(input.get_bracket_depth());
|
// TODO: I could insert CitationBracket entries in the context after each matched object to reduce the scanning done for counting brackets which should be more efficient.
|
||||||
let parser_context =
|
let parser_context = context
|
||||||
context.with_additional_node(ContextElement::ExitMatcherNode(ExitMatcherNode {
|
.with_additional_node(ContextElement::CitationBracket(CitationBracket {
|
||||||
|
position: input,
|
||||||
|
depth: 0,
|
||||||
|
}))
|
||||||
|
.with_additional_node(ContextElement::ExitMatcherNode(ExitMatcherNode {
|
||||||
class: ExitClass::Gamma,
|
class: ExitClass::Gamma,
|
||||||
exit_matcher: &exit_with_depth,
|
exit_matcher: &global_prefix_end,
|
||||||
}));
|
}));
|
||||||
let (remaining, (children, _exit_contents)) = verify(
|
let (remaining, (children, _exit_contents)) = verify(
|
||||||
many_till(
|
many_till(
|
||||||
@@ -102,24 +104,28 @@ fn global_prefix<'r, 's>(
|
|||||||
Ok((remaining, children))
|
Ok((remaining, children))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn global_prefix_end(
|
|
||||||
starting_bracket_depth: BracketDepth,
|
|
||||||
) -> impl for<'r, 's> Fn(Context<'r, 's>, OrgSource<'s>) -> Res<OrgSource<'s>, OrgSource<'s>> {
|
|
||||||
move |context: Context, input: OrgSource<'_>| {
|
|
||||||
_global_prefix_end(context, input, starting_bracket_depth)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
|
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
|
||||||
fn _global_prefix_end<'r, 's>(
|
fn global_prefix_end<'r, 's>(
|
||||||
context: Context<'r, 's>,
|
context: Context<'r, 's>,
|
||||||
input: OrgSource<'s>,
|
input: OrgSource<'s>,
|
||||||
starting_bracket_depth: BracketDepth,
|
|
||||||
) -> Res<OrgSource<'s>, OrgSource<'s>> {
|
) -> Res<OrgSource<'s>, OrgSource<'s>> {
|
||||||
let current_depth = input.get_bracket_depth() - starting_bracket_depth;
|
let context_depth = get_bracket_depth(context)
|
||||||
if current_depth < 0 {
|
.expect("This function should only be called from inside a citation.");
|
||||||
// This shouldn't be possible because if depth is 0 then a closing bracket should end the citation.
|
let text_since_context_entry = get_consumed(context_depth.position, input);
|
||||||
unreachable!("Exceeded citation global prefix bracket depth.")
|
let mut current_depth = context_depth.depth;
|
||||||
|
for c in Into::<&str>::into(text_since_context_entry).chars() {
|
||||||
|
match c {
|
||||||
|
'[' => {
|
||||||
|
current_depth += 1;
|
||||||
|
}
|
||||||
|
']' if current_depth == 0 => {
|
||||||
|
panic!("Exceeded citation global prefix bracket depth.")
|
||||||
|
}
|
||||||
|
']' if current_depth > 0 => {
|
||||||
|
current_depth -= 1;
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if current_depth == 0 {
|
if current_depth == 0 {
|
||||||
let close_bracket = tag::<&str, OrgSource<'_>, CustomError<OrgSource<'_>>>("]")(input);
|
let close_bracket = tag::<&str, OrgSource<'_>, CustomError<OrgSource<'_>>>("]")(input);
|
||||||
@@ -138,11 +144,15 @@ fn global_suffix<'r, 's>(
|
|||||||
context: Context<'r, 's>,
|
context: Context<'r, 's>,
|
||||||
input: OrgSource<'s>,
|
input: OrgSource<'s>,
|
||||||
) -> Res<OrgSource<'s>, Vec<Object<'s>>> {
|
) -> Res<OrgSource<'s>, Vec<Object<'s>>> {
|
||||||
let exit_with_depth = global_suffix_end(input.get_bracket_depth());
|
// TODO: I could insert CitationBracket entries in the context after each matched object to reduce the scanning done for counting brackets which should be more efficient.
|
||||||
let parser_context =
|
let parser_context = context
|
||||||
context.with_additional_node(ContextElement::ExitMatcherNode(ExitMatcherNode {
|
.with_additional_node(ContextElement::CitationBracket(CitationBracket {
|
||||||
|
position: input,
|
||||||
|
depth: 0,
|
||||||
|
}))
|
||||||
|
.with_additional_node(ContextElement::ExitMatcherNode(ExitMatcherNode {
|
||||||
class: ExitClass::Gamma,
|
class: ExitClass::Gamma,
|
||||||
exit_matcher: &exit_with_depth,
|
exit_matcher: &global_suffix_end,
|
||||||
}));
|
}));
|
||||||
let (remaining, (children, _exit_contents)) = verify(
|
let (remaining, (children, _exit_contents)) = verify(
|
||||||
many_till(
|
many_till(
|
||||||
@@ -154,24 +164,28 @@ fn global_suffix<'r, 's>(
|
|||||||
Ok((remaining, children))
|
Ok((remaining, children))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn global_suffix_end(
|
|
||||||
starting_bracket_depth: BracketDepth,
|
|
||||||
) -> impl for<'r, 's> Fn(Context<'r, 's>, OrgSource<'s>) -> Res<OrgSource<'s>, OrgSource<'s>> {
|
|
||||||
move |context: Context, input: OrgSource<'_>| {
|
|
||||||
_global_suffix_end(context, input, starting_bracket_depth)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
|
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
|
||||||
fn _global_suffix_end<'r, 's>(
|
fn global_suffix_end<'r, 's>(
|
||||||
context: Context<'r, 's>,
|
context: Context<'r, 's>,
|
||||||
input: OrgSource<'s>,
|
input: OrgSource<'s>,
|
||||||
starting_bracket_depth: BracketDepth,
|
|
||||||
) -> Res<OrgSource<'s>, OrgSource<'s>> {
|
) -> Res<OrgSource<'s>, OrgSource<'s>> {
|
||||||
let current_depth = input.get_bracket_depth() - starting_bracket_depth;
|
let context_depth = get_bracket_depth(context)
|
||||||
if current_depth < 0 {
|
.expect("This function should only be called from inside a citation.");
|
||||||
// This shouldn't be possible because if depth is 0 then a closing bracket should end the citation.
|
let text_since_context_entry = get_consumed(context_depth.position, input);
|
||||||
unreachable!("Exceeded citation global suffix bracket depth.")
|
let mut current_depth = context_depth.depth;
|
||||||
|
for c in Into::<&str>::into(text_since_context_entry).chars() {
|
||||||
|
match c {
|
||||||
|
'[' => {
|
||||||
|
current_depth += 1;
|
||||||
|
}
|
||||||
|
']' if current_depth == 0 => {
|
||||||
|
panic!("Exceeded citation global suffix bracket depth.")
|
||||||
|
}
|
||||||
|
']' if current_depth > 0 => {
|
||||||
|
current_depth -= 1;
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if current_depth == 0 {
|
if current_depth == 0 {
|
||||||
let close_bracket = tag::<&str, OrgSource<'_>, CustomError<OrgSource<'_>>>("]")(input);
|
let close_bracket = tag::<&str, OrgSource<'_>, CustomError<OrgSource<'_>>>("]")(input);
|
||||||
|
|||||||
@@ -10,15 +10,14 @@ use nom::multi::many_till;
|
|||||||
use nom::sequence::preceded;
|
use nom::sequence::preceded;
|
||||||
use nom::sequence::tuple;
|
use nom::sequence::tuple;
|
||||||
|
|
||||||
use super::org_source::BracketDepth;
|
|
||||||
use super::org_source::OrgSource;
|
use super::org_source::OrgSource;
|
||||||
use super::Context;
|
use super::Context;
|
||||||
use crate::error::CustomError;
|
use crate::error::CustomError;
|
||||||
use crate::error::MyError;
|
|
||||||
use crate::error::Res;
|
use crate::error::Res;
|
||||||
use crate::parser::exiting::ExitClass;
|
use crate::parser::exiting::ExitClass;
|
||||||
use crate::parser::object::CitationReference;
|
use crate::parser::object::CitationReference;
|
||||||
use crate::parser::object_parser::minimal_set_object;
|
use crate::parser::object_parser::minimal_set_object;
|
||||||
|
use crate::parser::parser_context::CitationBracket;
|
||||||
use crate::parser::parser_context::ContextElement;
|
use crate::parser::parser_context::ContextElement;
|
||||||
use crate::parser::parser_context::ExitMatcherNode;
|
use crate::parser::parser_context::ExitMatcherNode;
|
||||||
use crate::parser::parser_with_context::parser_with_context;
|
use crate::parser::parser_with_context::parser_with_context;
|
||||||
@@ -32,11 +31,9 @@ pub fn citation_reference<'r, 's>(
|
|||||||
context: Context<'r, 's>,
|
context: Context<'r, 's>,
|
||||||
input: OrgSource<'s>,
|
input: OrgSource<'s>,
|
||||||
) -> Res<OrgSource<'s>, CitationReference<'s>> {
|
) -> Res<OrgSource<'s>, CitationReference<'s>> {
|
||||||
let (remaining, _prefix) =
|
let (remaining, _prefix) = opt(parser_with_context!(key_prefix)(context))(input)?;
|
||||||
must_balance_bracket(opt(parser_with_context!(key_prefix)(context)))(input)?;
|
|
||||||
let (remaining, _key) = parser_with_context!(citation_reference_key)(context)(remaining)?;
|
let (remaining, _key) = parser_with_context!(citation_reference_key)(context)(remaining)?;
|
||||||
let (remaining, _suffix) =
|
let (remaining, _suffix) = opt(parser_with_context!(key_suffix)(context))(remaining)?;
|
||||||
must_balance_bracket(opt(parser_with_context!(key_suffix)(context)))(remaining)?;
|
|
||||||
let source = get_consumed(input, remaining);
|
let source = get_consumed(input, remaining);
|
||||||
|
|
||||||
Ok((
|
Ok((
|
||||||
@@ -72,11 +69,15 @@ fn key_prefix<'r, 's>(
|
|||||||
context: Context<'r, 's>,
|
context: Context<'r, 's>,
|
||||||
input: OrgSource<'s>,
|
input: OrgSource<'s>,
|
||||||
) -> Res<OrgSource<'s>, Vec<Object<'s>>> {
|
) -> Res<OrgSource<'s>, Vec<Object<'s>>> {
|
||||||
let exit_with_depth = key_prefix_end(input.get_bracket_depth());
|
// TODO: I could insert CitationBracket entries in the context after each matched object to reduce the scanning done for counting brackets which should be more efficient.
|
||||||
let parser_context =
|
let parser_context = context
|
||||||
context.with_additional_node(ContextElement::ExitMatcherNode(ExitMatcherNode {
|
.with_additional_node(ContextElement::CitationBracket(CitationBracket {
|
||||||
|
position: input,
|
||||||
|
depth: 0,
|
||||||
|
}))
|
||||||
|
.with_additional_node(ContextElement::ExitMatcherNode(ExitMatcherNode {
|
||||||
class: ExitClass::Gamma,
|
class: ExitClass::Gamma,
|
||||||
exit_matcher: &exit_with_depth,
|
exit_matcher: &key_prefix_end,
|
||||||
}));
|
}));
|
||||||
let (remaining, (children, _exit_contents)) = verify(
|
let (remaining, (children, _exit_contents)) = verify(
|
||||||
many_till(
|
many_till(
|
||||||
@@ -93,11 +94,15 @@ fn key_suffix<'r, 's>(
|
|||||||
context: Context<'r, 's>,
|
context: Context<'r, 's>,
|
||||||
input: OrgSource<'s>,
|
input: OrgSource<'s>,
|
||||||
) -> Res<OrgSource<'s>, Vec<Object<'s>>> {
|
) -> Res<OrgSource<'s>, Vec<Object<'s>>> {
|
||||||
let exit_with_depth = key_suffix_end(input.get_bracket_depth());
|
// TODO: I could insert CitationBracket entries in the context after each matched object to reduce the scanning done for counting brackets which should be more efficient.
|
||||||
let parser_context =
|
let parser_context = context
|
||||||
context.with_additional_node(ContextElement::ExitMatcherNode(ExitMatcherNode {
|
.with_additional_node(ContextElement::CitationBracket(CitationBracket {
|
||||||
|
position: input,
|
||||||
|
depth: 0,
|
||||||
|
}))
|
||||||
|
.with_additional_node(ContextElement::ExitMatcherNode(ExitMatcherNode {
|
||||||
class: ExitClass::Gamma,
|
class: ExitClass::Gamma,
|
||||||
exit_matcher: &exit_with_depth,
|
exit_matcher: &key_suffix_end,
|
||||||
}));
|
}));
|
||||||
let (remaining, (children, _exit_contents)) = verify(
|
let (remaining, (children, _exit_contents)) = verify(
|
||||||
many_till(
|
many_till(
|
||||||
@@ -109,24 +114,39 @@ fn key_suffix<'r, 's>(
|
|||||||
Ok((remaining, children))
|
Ok((remaining, children))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn key_prefix_end(
|
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
|
||||||
starting_bracket_depth: BracketDepth,
|
pub fn get_bracket_depth<'r, 's>(context: Context<'r, 's>) -> Option<&'r CitationBracket<'s>> {
|
||||||
) -> impl for<'r, 's> Fn(Context<'r, 's>, OrgSource<'s>) -> Res<OrgSource<'s>, OrgSource<'s>> {
|
for node in context.iter() {
|
||||||
move |context: Context, input: OrgSource<'_>| {
|
match node.get_data() {
|
||||||
_key_prefix_end(context, input, starting_bracket_depth)
|
ContextElement::CitationBracket(depth) => return Some(depth),
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
None
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
|
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
|
||||||
fn _key_prefix_end<'r, 's>(
|
fn key_prefix_end<'r, 's>(
|
||||||
context: Context<'r, 's>,
|
context: Context<'r, 's>,
|
||||||
input: OrgSource<'s>,
|
input: OrgSource<'s>,
|
||||||
starting_bracket_depth: BracketDepth,
|
|
||||||
) -> Res<OrgSource<'s>, OrgSource<'s>> {
|
) -> Res<OrgSource<'s>, OrgSource<'s>> {
|
||||||
let current_depth = input.get_bracket_depth() - starting_bracket_depth;
|
let context_depth = get_bracket_depth(context)
|
||||||
if current_depth < 0 {
|
.expect("This function should only be called from inside a citation reference.");
|
||||||
// This shouldn't be possible because if depth is 0 then a closing bracket should end the citation.
|
let text_since_context_entry = get_consumed(context_depth.position, input);
|
||||||
unreachable!("Exceeded citation key prefix bracket depth.")
|
let mut current_depth = context_depth.depth;
|
||||||
|
for c in Into::<&str>::into(text_since_context_entry).chars() {
|
||||||
|
match c {
|
||||||
|
'[' => {
|
||||||
|
current_depth += 1;
|
||||||
|
}
|
||||||
|
']' if current_depth == 0 => {
|
||||||
|
panic!("Exceeded citation reference key prefix bracket depth.")
|
||||||
|
}
|
||||||
|
']' if current_depth > 0 => {
|
||||||
|
current_depth -= 1;
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if current_depth == 0 {
|
if current_depth == 0 {
|
||||||
let close_bracket = tag::<&str, OrgSource<'_>, CustomError<OrgSource<'_>>>("]")(input);
|
let close_bracket = tag::<&str, OrgSource<'_>, CustomError<OrgSource<'_>>>("]")(input);
|
||||||
@@ -140,24 +160,28 @@ fn _key_prefix_end<'r, 's>(
|
|||||||
))(input)
|
))(input)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn key_suffix_end(
|
|
||||||
starting_bracket_depth: BracketDepth,
|
|
||||||
) -> impl for<'r, 's> Fn(Context<'r, 's>, OrgSource<'s>) -> Res<OrgSource<'s>, OrgSource<'s>> {
|
|
||||||
move |context: Context, input: OrgSource<'_>| {
|
|
||||||
_key_suffix_end(context, input, starting_bracket_depth)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
|
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
|
||||||
fn _key_suffix_end<'r, 's>(
|
fn key_suffix_end<'r, 's>(
|
||||||
_context: Context<'r, 's>,
|
context: Context<'r, 's>,
|
||||||
input: OrgSource<'s>,
|
input: OrgSource<'s>,
|
||||||
starting_bracket_depth: BracketDepth,
|
|
||||||
) -> Res<OrgSource<'s>, OrgSource<'s>> {
|
) -> Res<OrgSource<'s>, OrgSource<'s>> {
|
||||||
let current_depth = input.get_bracket_depth() - starting_bracket_depth;
|
let context_depth = get_bracket_depth(context)
|
||||||
if current_depth < 0 {
|
.expect("This function should only be called from inside a citation reference.");
|
||||||
// This shouldn't be possible because if depth is 0 then a closing bracket should end the citation.
|
let text_since_context_entry = get_consumed(context_depth.position, input);
|
||||||
unreachable!("Exceeded citation key suffix bracket depth.")
|
let mut current_depth = context_depth.depth;
|
||||||
|
for c in Into::<&str>::into(text_since_context_entry).chars() {
|
||||||
|
match c {
|
||||||
|
'[' => {
|
||||||
|
current_depth += 1;
|
||||||
|
}
|
||||||
|
']' if current_depth == 0 => {
|
||||||
|
panic!("Exceeded citation reference key prefix bracket depth.")
|
||||||
|
}
|
||||||
|
']' if current_depth > 0 => {
|
||||||
|
current_depth -= 1;
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if current_depth == 0 {
|
if current_depth == 0 {
|
||||||
let close_bracket = tag::<&str, OrgSource<'_>, CustomError<OrgSource<'_>>>("]")(input);
|
let close_bracket = tag::<&str, OrgSource<'_>, CustomError<OrgSource<'_>>>("]")(input);
|
||||||
@@ -167,21 +191,3 @@ fn _key_suffix_end<'r, 's>(
|
|||||||
}
|
}
|
||||||
tag(";")(input)
|
tag(";")(input)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn must_balance_bracket<'s, F, O>(
|
|
||||||
mut inner: F,
|
|
||||||
) -> impl FnMut(OrgSource<'s>) -> Res<OrgSource<'s>, O>
|
|
||||||
where
|
|
||||||
F: FnMut(OrgSource<'s>) -> Res<OrgSource<'s>, O>,
|
|
||||||
{
|
|
||||||
move |input: OrgSource<'_>| {
|
|
||||||
let pre_bracket_depth = input.get_bracket_depth();
|
|
||||||
let (remaining, output) = inner(input)?;
|
|
||||||
if remaining.get_bracket_depth() - pre_bracket_depth != 0 {
|
|
||||||
return Err(nom::Err::Error(CustomError::MyError(MyError(
|
|
||||||
"UnbalancedBrackets".into(),
|
|
||||||
))));
|
|
||||||
}
|
|
||||||
Ok((remaining, output))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -1,8 +1,6 @@
|
|||||||
use nom::branch::alt;
|
use nom::branch::alt;
|
||||||
use nom::bytes::complete::tag;
|
use nom::bytes::complete::tag;
|
||||||
use nom::character::complete::anychar;
|
|
||||||
use nom::character::complete::line_ending;
|
use nom::character::complete::line_ending;
|
||||||
use nom::character::complete::space0;
|
|
||||||
use nom::character::complete::space1;
|
use nom::character::complete::space1;
|
||||||
use nom::combinator::eof;
|
use nom::combinator::eof;
|
||||||
use nom::combinator::map;
|
use nom::combinator::map;
|
||||||
@@ -14,7 +12,6 @@ use nom::multi::many0;
|
|||||||
use nom::multi::many1;
|
use nom::multi::many1;
|
||||||
use nom::multi::many1_count;
|
use nom::multi::many1_count;
|
||||||
use nom::multi::many_till;
|
use nom::multi::many_till;
|
||||||
use nom::multi::separated_list1;
|
|
||||||
use nom::sequence::tuple;
|
use nom::sequence::tuple;
|
||||||
|
|
||||||
use super::element::Element;
|
use super::element::Element;
|
||||||
@@ -53,10 +50,7 @@ pub struct Document<'s> {
|
|||||||
pub struct Heading<'s> {
|
pub struct Heading<'s> {
|
||||||
pub source: &'s str,
|
pub source: &'s str,
|
||||||
pub stars: usize,
|
pub stars: usize,
|
||||||
pub todo_keyword: Option<&'s str>,
|
|
||||||
// TODO: add todo-type enum
|
|
||||||
pub title: Vec<Object<'s>>,
|
pub title: Vec<Object<'s>>,
|
||||||
pub tags: Vec<&'s str>,
|
|
||||||
pub children: Vec<DocumentElement<'s>>,
|
pub children: Vec<DocumentElement<'s>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -139,7 +133,7 @@ fn _document<'r, 's>(
|
|||||||
input: OrgSource<'s>,
|
input: OrgSource<'s>,
|
||||||
) -> Res<OrgSource<'s>, Document<'s>> {
|
) -> Res<OrgSource<'s>, Document<'s>> {
|
||||||
let zeroth_section_matcher = parser_with_context!(zeroth_section)(context);
|
let zeroth_section_matcher = parser_with_context!(zeroth_section)(context);
|
||||||
let heading_matcher = parser_with_context!(heading(0))(context);
|
let heading_matcher = parser_with_context!(heading)(context);
|
||||||
let (remaining, _blank_lines) = many0(blank_line)(input)?;
|
let (remaining, _blank_lines) = many0(blank_line)(input)?;
|
||||||
let (remaining, zeroth_section) = opt(zeroth_section_matcher)(remaining)?;
|
let (remaining, zeroth_section) = opt(zeroth_section_matcher)(remaining)?;
|
||||||
let (remaining, children) = many0(heading_matcher)(remaining)?;
|
let (remaining, children) = many0(heading_matcher)(remaining)?;
|
||||||
@@ -260,149 +254,69 @@ fn section<'r, 's>(
|
|||||||
|
|
||||||
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
|
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
|
||||||
fn section_end<'r, 's>(
|
fn section_end<'r, 's>(
|
||||||
_context: Context<'r, 's>,
|
context: Context<'r, 's>,
|
||||||
input: OrgSource<'s>,
|
input: OrgSource<'s>,
|
||||||
) -> Res<OrgSource<'s>, OrgSource<'s>> {
|
) -> Res<OrgSource<'s>, OrgSource<'s>> {
|
||||||
recognize(detect_headline)(input)
|
let headline_matcher = parser_with_context!(headline)(context);
|
||||||
}
|
recognize(headline_matcher)(input)
|
||||||
|
|
||||||
const fn heading(
|
|
||||||
parent_stars: usize,
|
|
||||||
) -> impl for<'r, 's> Fn(Context<'r, 's>, OrgSource<'s>) -> Res<OrgSource<'s>, Heading<'s>> {
|
|
||||||
move |context: Context, input: OrgSource<'_>| _heading(context, input, parent_stars)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
|
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
|
||||||
fn _heading<'r, 's>(
|
fn heading<'r, 's>(
|
||||||
context: Context<'r, 's>,
|
context: Context<'r, 's>,
|
||||||
input: OrgSource<'s>,
|
input: OrgSource<'s>,
|
||||||
parent_stars: usize,
|
|
||||||
) -> Res<OrgSource<'s>, Heading<'s>> {
|
) -> Res<OrgSource<'s>, Heading<'s>> {
|
||||||
not(|i| context.check_exit_matcher(i))(input)?;
|
not(|i| context.check_exit_matcher(i))(input)?;
|
||||||
let (remaining, (star_count, _ws, maybe_todo_keyword, title, heading_tags)) =
|
let (remaining, (star_count, _ws, title)) = headline(context, input)?;
|
||||||
headline(context, input, parent_stars)?;
|
|
||||||
let section_matcher = parser_with_context!(section)(context);
|
let section_matcher = parser_with_context!(section)(context);
|
||||||
let heading_matcher = parser_with_context!(heading(star_count))(context);
|
let heading_matcher = parser_with_context!(heading)(context);
|
||||||
let (remaining, maybe_section) =
|
let (remaining, children) = many0(alt((
|
||||||
opt(map(section_matcher, DocumentElement::Section))(remaining)?;
|
map(
|
||||||
let (remaining, mut children) =
|
verify(heading_matcher, |h| h.stars > star_count),
|
||||||
many0(map(heading_matcher, DocumentElement::Heading))(remaining)?;
|
DocumentElement::Heading,
|
||||||
if let Some(section) = maybe_section {
|
),
|
||||||
children.insert(0, section);
|
map(section_matcher, DocumentElement::Section),
|
||||||
}
|
)))(remaining)?;
|
||||||
let remaining = if children.is_empty() {
|
|
||||||
// Support empty headings
|
|
||||||
let (remain, _ws) = many0(blank_line)(remaining)?;
|
|
||||||
remain
|
|
||||||
} else {
|
|
||||||
remaining
|
|
||||||
};
|
|
||||||
|
|
||||||
let source = get_consumed(input, remaining);
|
let source = get_consumed(input, remaining);
|
||||||
Ok((
|
Ok((
|
||||||
remaining,
|
remaining,
|
||||||
Heading {
|
Heading {
|
||||||
source: source.into(),
|
source: source.into(),
|
||||||
stars: star_count,
|
stars: star_count,
|
||||||
todo_keyword: maybe_todo_keyword
|
|
||||||
.map(|(todo_keyword, _ws)| Into::<&str>::into(todo_keyword)),
|
|
||||||
title,
|
title,
|
||||||
tags: heading_tags,
|
|
||||||
children,
|
children,
|
||||||
},
|
},
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
|
|
||||||
fn detect_headline<'s>(input: OrgSource<'s>) -> Res<OrgSource<'s>, ()> {
|
|
||||||
tuple((start_of_line, many1(tag("*")), space1))(input)?;
|
|
||||||
Ok((input, ()))
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
|
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
|
||||||
fn headline<'r, 's>(
|
fn headline<'r, 's>(
|
||||||
context: Context<'r, 's>,
|
context: Context<'r, 's>,
|
||||||
input: OrgSource<'s>,
|
input: OrgSource<'s>,
|
||||||
parent_stars: usize,
|
) -> Res<OrgSource<'s>, (usize, OrgSource<'s>, Vec<Object<'s>>)> {
|
||||||
) -> Res<
|
|
||||||
OrgSource<'s>,
|
|
||||||
(
|
|
||||||
usize,
|
|
||||||
OrgSource<'s>,
|
|
||||||
Option<(OrgSource<'s>, OrgSource<'s>)>,
|
|
||||||
Vec<Object<'s>>,
|
|
||||||
Vec<&'s str>,
|
|
||||||
),
|
|
||||||
> {
|
|
||||||
let parser_context =
|
let parser_context =
|
||||||
context.with_additional_node(ContextElement::ExitMatcherNode(ExitMatcherNode {
|
context.with_additional_node(ContextElement::ExitMatcherNode(ExitMatcherNode {
|
||||||
class: ExitClass::Document,
|
class: ExitClass::Document,
|
||||||
exit_matcher: &headline_title_end,
|
exit_matcher: &headline_end,
|
||||||
}));
|
}));
|
||||||
let standard_set_object_matcher = parser_with_context!(standard_set_object)(&parser_context);
|
let standard_set_object_matcher = parser_with_context!(standard_set_object)(&parser_context);
|
||||||
|
|
||||||
let (
|
let (remaining, (_sol, star_count, ws, title, _line_ending)) = tuple((
|
||||||
remaining,
|
|
||||||
(_sol, star_count, ws, maybe_todo_keyword, title, maybe_tags, _ws, _line_ending),
|
|
||||||
) = tuple((
|
|
||||||
start_of_line,
|
start_of_line,
|
||||||
verify(many1_count(tag("*")), |star_count| {
|
many1_count(tag("*")),
|
||||||
*star_count > parent_stars
|
|
||||||
}),
|
|
||||||
space1,
|
space1,
|
||||||
opt(tuple((heading_keyword, space1))),
|
|
||||||
many1(standard_set_object_matcher),
|
many1(standard_set_object_matcher),
|
||||||
opt(tuple((space0, tags))),
|
|
||||||
space0,
|
|
||||||
alt((line_ending, eof)),
|
alt((line_ending, eof)),
|
||||||
))(input)?;
|
))(input)?;
|
||||||
Ok((
|
Ok((remaining, (star_count, ws, title)))
|
||||||
remaining,
|
|
||||||
(
|
|
||||||
star_count,
|
|
||||||
ws,
|
|
||||||
maybe_todo_keyword,
|
|
||||||
title,
|
|
||||||
maybe_tags
|
|
||||||
.map(|(_ws, tags)| {
|
|
||||||
tags.into_iter()
|
|
||||||
.map(|single_tag| Into::<&str>::into(single_tag))
|
|
||||||
.collect()
|
|
||||||
})
|
|
||||||
.unwrap_or(Vec::new()),
|
|
||||||
),
|
|
||||||
))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
|
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
|
||||||
fn headline_title_end<'r, 's>(
|
fn headline_end<'r, 's>(
|
||||||
_context: Context<'r, 's>,
|
_context: Context<'r, 's>,
|
||||||
input: OrgSource<'s>,
|
input: OrgSource<'s>,
|
||||||
) -> Res<OrgSource<'s>, OrgSource<'s>> {
|
) -> Res<OrgSource<'s>, OrgSource<'s>> {
|
||||||
recognize(tuple((
|
line_ending(input)
|
||||||
opt(tuple((space0, tags, space0))),
|
|
||||||
alt((line_ending, eof)),
|
|
||||||
)))(input)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
|
|
||||||
fn tags<'s>(input: OrgSource<'s>) -> Res<OrgSource<'s>, Vec<OrgSource<'s>>> {
|
|
||||||
let (remaining, (_open, tags, _close)) =
|
|
||||||
tuple((tag(":"), separated_list1(tag(":"), single_tag), tag(":")))(input)?;
|
|
||||||
Ok((remaining, tags))
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
|
|
||||||
fn single_tag<'r, 's>(input: OrgSource<'s>) -> Res<OrgSource<'s>, OrgSource<'s>> {
|
|
||||||
recognize(many1(verify(anychar, |c| {
|
|
||||||
c.is_alphanumeric() || "_@#%".contains(*c)
|
|
||||||
})))(input)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
|
|
||||||
fn heading_keyword<'s>(input: OrgSource<'s>) -> Res<OrgSource<'s>, OrgSource<'s>> {
|
|
||||||
// TODO: This should take into account the value of "#+TODO:" ref https://orgmode.org/manual/Per_002dfile-keywords.html and possibly the configurable variable org-todo-keywords ref https://orgmode.org/manual/Workflow-states.html. Case is significant.
|
|
||||||
alt((tag("TODO"), tag("DONE")))(input)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'s> Document<'s> {
|
impl<'s> Document<'s> {
|
||||||
|
|||||||
@@ -12,7 +12,6 @@ use super::fixed_width_area::fixed_width_area;
|
|||||||
use super::footnote_definition::footnote_definition;
|
use super::footnote_definition::footnote_definition;
|
||||||
use super::greater_block::greater_block;
|
use super::greater_block::greater_block;
|
||||||
use super::horizontal_rule::horizontal_rule;
|
use super::horizontal_rule::horizontal_rule;
|
||||||
use super::keyword::affiliated_keyword;
|
|
||||||
use super::keyword::keyword;
|
use super::keyword::keyword;
|
||||||
use super::latex_environment::latex_environment;
|
use super::latex_environment::latex_environment;
|
||||||
use super::lesser_block::comment_block;
|
use super::lesser_block::comment_block;
|
||||||
@@ -63,12 +62,10 @@ fn _element<'r, 's>(
|
|||||||
let fixed_width_area_matcher = parser_with_context!(fixed_width_area)(context);
|
let fixed_width_area_matcher = parser_with_context!(fixed_width_area)(context);
|
||||||
let horizontal_rule_matcher = parser_with_context!(horizontal_rule)(context);
|
let horizontal_rule_matcher = parser_with_context!(horizontal_rule)(context);
|
||||||
let keyword_matcher = parser_with_context!(keyword)(context);
|
let keyword_matcher = parser_with_context!(keyword)(context);
|
||||||
let affiliated_keyword_matcher = parser_with_context!(affiliated_keyword)(context);
|
|
||||||
let paragraph_matcher = parser_with_context!(paragraph)(context);
|
let paragraph_matcher = parser_with_context!(paragraph)(context);
|
||||||
let latex_environment_matcher = parser_with_context!(latex_environment)(context);
|
let latex_environment_matcher = parser_with_context!(latex_environment)(context);
|
||||||
|
|
||||||
// TODO: Affiliated keywords cannot be on comments, clocks, headings, inlinetasks, items, node properties, planning, property drawers, sections, and table rows
|
let (remaining, mut affiliated_keywords) = many0(keyword_matcher)(input)?;
|
||||||
let (remaining, mut affiliated_keywords) = many0(affiliated_keyword_matcher)(input)?;
|
|
||||||
let (remaining, mut element) = match alt((
|
let (remaining, mut element) = match alt((
|
||||||
map(plain_list_matcher, Element::PlainList),
|
map(plain_list_matcher, Element::PlainList),
|
||||||
map(greater_block_matcher, Element::GreaterBlock),
|
map(greater_block_matcher, Element::GreaterBlock),
|
||||||
@@ -87,7 +84,6 @@ fn _element<'r, 's>(
|
|||||||
map(fixed_width_area_matcher, Element::FixedWidthArea),
|
map(fixed_width_area_matcher, Element::FixedWidthArea),
|
||||||
map(horizontal_rule_matcher, Element::HorizontalRule),
|
map(horizontal_rule_matcher, Element::HorizontalRule),
|
||||||
map(latex_environment_matcher, Element::LatexEnvironment),
|
map(latex_environment_matcher, Element::LatexEnvironment),
|
||||||
map(keyword_matcher, Element::Keyword),
|
|
||||||
))(remaining)
|
))(remaining)
|
||||||
{
|
{
|
||||||
the_ok @ Ok(_) => the_ok,
|
the_ok @ Ok(_) => the_ok,
|
||||||
@@ -97,12 +93,12 @@ fn _element<'r, 's>(
|
|||||||
the_ok @ Ok(_) => the_ok,
|
the_ok @ Ok(_) => the_ok,
|
||||||
Err(_) => {
|
Err(_) => {
|
||||||
affiliated_keywords.clear();
|
affiliated_keywords.clear();
|
||||||
map(affiliated_keyword_matcher, Element::Keyword)(input)
|
map(keyword_matcher, Element::Keyword)(input)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
affiliated_keywords.clear();
|
affiliated_keywords.clear();
|
||||||
map(affiliated_keyword_matcher, Element::Keyword)(input)
|
map(keyword_matcher, Element::Keyword)(input)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}?;
|
}?;
|
||||||
|
|||||||
@@ -9,429 +9,11 @@ use nom::combinator::recognize;
|
|||||||
|
|
||||||
use super::org_source::OrgSource;
|
use super::org_source::OrgSource;
|
||||||
use super::Context;
|
use super::Context;
|
||||||
use crate::error::CustomError;
|
|
||||||
use crate::error::MyError;
|
|
||||||
use crate::error::Res;
|
use crate::error::Res;
|
||||||
use crate::parser::object::Entity;
|
use crate::parser::object::Entity;
|
||||||
|
use crate::parser::parser_with_context::parser_with_context;
|
||||||
use crate::parser::util::get_consumed;
|
use crate::parser::util::get_consumed;
|
||||||
|
|
||||||
// TODO: Make this a user-provided variable corresponding to elisp's org-entities
|
|
||||||
const ORG_ENTITIES: [&'static str; 413] = [
|
|
||||||
"Agrave",
|
|
||||||
"agrave",
|
|
||||||
"Aacute",
|
|
||||||
"aacute",
|
|
||||||
"Acirc",
|
|
||||||
"acirc",
|
|
||||||
"Amacr",
|
|
||||||
"amacr",
|
|
||||||
"Atilde",
|
|
||||||
"atilde",
|
|
||||||
"Auml",
|
|
||||||
"auml",
|
|
||||||
"Aring",
|
|
||||||
"AA",
|
|
||||||
"aring",
|
|
||||||
"AElig",
|
|
||||||
"aelig",
|
|
||||||
"Ccedil",
|
|
||||||
"ccedil",
|
|
||||||
"Egrave",
|
|
||||||
"egrave",
|
|
||||||
"Eacute",
|
|
||||||
"eacute",
|
|
||||||
"Ecirc",
|
|
||||||
"ecirc",
|
|
||||||
"Euml",
|
|
||||||
"euml",
|
|
||||||
"Igrave",
|
|
||||||
"igrave",
|
|
||||||
"Iacute",
|
|
||||||
"iacute",
|
|
||||||
"Idot",
|
|
||||||
"inodot",
|
|
||||||
"Icirc",
|
|
||||||
"icirc",
|
|
||||||
"Iuml",
|
|
||||||
"iuml",
|
|
||||||
"Ntilde",
|
|
||||||
"ntilde",
|
|
||||||
"Ograve",
|
|
||||||
"ograve",
|
|
||||||
"Oacute",
|
|
||||||
"oacute",
|
|
||||||
"Ocirc",
|
|
||||||
"ocirc",
|
|
||||||
"Otilde",
|
|
||||||
"otilde",
|
|
||||||
"Ouml",
|
|
||||||
"ouml",
|
|
||||||
"Oslash",
|
|
||||||
"oslash",
|
|
||||||
"OElig",
|
|
||||||
"oelig",
|
|
||||||
"Scaron",
|
|
||||||
"scaron",
|
|
||||||
"szlig",
|
|
||||||
"Ugrave",
|
|
||||||
"ugrave",
|
|
||||||
"Uacute",
|
|
||||||
"uacute",
|
|
||||||
"Ucirc",
|
|
||||||
"ucirc",
|
|
||||||
"Uuml",
|
|
||||||
"uuml",
|
|
||||||
"Yacute",
|
|
||||||
"yacute",
|
|
||||||
"Yuml",
|
|
||||||
"yuml",
|
|
||||||
"fnof",
|
|
||||||
"real",
|
|
||||||
"image",
|
|
||||||
"weierp",
|
|
||||||
"ell",
|
|
||||||
"imath",
|
|
||||||
"jmath",
|
|
||||||
"Alpha",
|
|
||||||
"alpha",
|
|
||||||
"Beta",
|
|
||||||
"beta",
|
|
||||||
"Gamma",
|
|
||||||
"gamma",
|
|
||||||
"Delta",
|
|
||||||
"delta",
|
|
||||||
"Epsilon",
|
|
||||||
"epsilon",
|
|
||||||
"varepsilon",
|
|
||||||
"Zeta",
|
|
||||||
"zeta",
|
|
||||||
"Eta",
|
|
||||||
"eta",
|
|
||||||
"Theta",
|
|
||||||
"theta",
|
|
||||||
"thetasym",
|
|
||||||
"vartheta",
|
|
||||||
"Iota",
|
|
||||||
"iota",
|
|
||||||
"Kappa",
|
|
||||||
"kappa",
|
|
||||||
"Lambda",
|
|
||||||
"lambda",
|
|
||||||
"Mu",
|
|
||||||
"mu",
|
|
||||||
"nu",
|
|
||||||
"Nu",
|
|
||||||
"Xi",
|
|
||||||
"xi",
|
|
||||||
"Omicron",
|
|
||||||
"omicron",
|
|
||||||
"Pi",
|
|
||||||
"pi",
|
|
||||||
"Rho",
|
|
||||||
"rho",
|
|
||||||
"Sigma",
|
|
||||||
"sigma",
|
|
||||||
"sigmaf",
|
|
||||||
"varsigma",
|
|
||||||
"Tau",
|
|
||||||
"Upsilon",
|
|
||||||
"upsih",
|
|
||||||
"upsilon",
|
|
||||||
"Phi",
|
|
||||||
"phi",
|
|
||||||
"varphi",
|
|
||||||
"Chi",
|
|
||||||
"chi",
|
|
||||||
"acutex",
|
|
||||||
"Psi",
|
|
||||||
"psi",
|
|
||||||
"tau",
|
|
||||||
"Omega",
|
|
||||||
"omega",
|
|
||||||
"piv",
|
|
||||||
"varpi",
|
|
||||||
"partial",
|
|
||||||
"alefsym",
|
|
||||||
"aleph",
|
|
||||||
"gimel",
|
|
||||||
"beth",
|
|
||||||
"dalet",
|
|
||||||
"ETH",
|
|
||||||
"eth",
|
|
||||||
"THORN",
|
|
||||||
"thorn",
|
|
||||||
"dots",
|
|
||||||
"cdots",
|
|
||||||
"hellip",
|
|
||||||
"middot",
|
|
||||||
"iexcl",
|
|
||||||
"iquest",
|
|
||||||
"shy",
|
|
||||||
"ndash",
|
|
||||||
"mdash",
|
|
||||||
"quot",
|
|
||||||
"acute",
|
|
||||||
"ldquo",
|
|
||||||
"rdquo",
|
|
||||||
"bdquo",
|
|
||||||
"lsquo",
|
|
||||||
"rsquo",
|
|
||||||
"sbquo",
|
|
||||||
"laquo",
|
|
||||||
"raquo",
|
|
||||||
"lsaquo",
|
|
||||||
"rsaquo",
|
|
||||||
"circ",
|
|
||||||
"vert",
|
|
||||||
"vbar",
|
|
||||||
"brvbar",
|
|
||||||
"S",
|
|
||||||
"sect",
|
|
||||||
"amp",
|
|
||||||
"lt",
|
|
||||||
"gt",
|
|
||||||
"tilde",
|
|
||||||
"slash",
|
|
||||||
"plus",
|
|
||||||
"under",
|
|
||||||
"equal",
|
|
||||||
"asciicirc",
|
|
||||||
"dagger",
|
|
||||||
"dag",
|
|
||||||
"Dagger",
|
|
||||||
"ddag",
|
|
||||||
"nbsp",
|
|
||||||
"ensp",
|
|
||||||
"emsp",
|
|
||||||
"thinsp",
|
|
||||||
"curren",
|
|
||||||
"cent",
|
|
||||||
"pound",
|
|
||||||
"yen",
|
|
||||||
"euro",
|
|
||||||
"EUR",
|
|
||||||
"dollar",
|
|
||||||
"USD",
|
|
||||||
"copy",
|
|
||||||
"reg",
|
|
||||||
"trade",
|
|
||||||
"minus",
|
|
||||||
"pm",
|
|
||||||
"plusmn",
|
|
||||||
"times",
|
|
||||||
"frasl",
|
|
||||||
"colon",
|
|
||||||
"div",
|
|
||||||
"frac12",
|
|
||||||
"frac14",
|
|
||||||
"frac34",
|
|
||||||
"permil",
|
|
||||||
"sup1",
|
|
||||||
"sup2",
|
|
||||||
"sup3",
|
|
||||||
"radic",
|
|
||||||
"sum",
|
|
||||||
"prod",
|
|
||||||
"micro",
|
|
||||||
"macr",
|
|
||||||
"deg",
|
|
||||||
"prime",
|
|
||||||
"Prime",
|
|
||||||
"infin",
|
|
||||||
"infty",
|
|
||||||
"prop",
|
|
||||||
"propto",
|
|
||||||
"not",
|
|
||||||
"neg",
|
|
||||||
"land",
|
|
||||||
"wedge",
|
|
||||||
"lor",
|
|
||||||
"vee",
|
|
||||||
"cap",
|
|
||||||
"cup",
|
|
||||||
"smile",
|
|
||||||
"frown",
|
|
||||||
"int",
|
|
||||||
"therefore",
|
|
||||||
"there4",
|
|
||||||
"because",
|
|
||||||
"sim",
|
|
||||||
"cong",
|
|
||||||
"simeq",
|
|
||||||
"asymp",
|
|
||||||
"approx",
|
|
||||||
"ne",
|
|
||||||
"neq",
|
|
||||||
"equiv",
|
|
||||||
"triangleq",
|
|
||||||
"le",
|
|
||||||
"leq",
|
|
||||||
"ge",
|
|
||||||
"geq",
|
|
||||||
"lessgtr",
|
|
||||||
"lesseqgtr",
|
|
||||||
"ll",
|
|
||||||
"Ll",
|
|
||||||
"lll",
|
|
||||||
"gg",
|
|
||||||
"Gg",
|
|
||||||
"ggg",
|
|
||||||
"prec",
|
|
||||||
"preceq",
|
|
||||||
"preccurlyeq",
|
|
||||||
"succ",
|
|
||||||
"succeq",
|
|
||||||
"succcurlyeq",
|
|
||||||
"sub",
|
|
||||||
"subset",
|
|
||||||
"sup",
|
|
||||||
"supset",
|
|
||||||
"nsub",
|
|
||||||
"sube",
|
|
||||||
"nsup",
|
|
||||||
"supe",
|
|
||||||
"setminus",
|
|
||||||
"forall",
|
|
||||||
"exist",
|
|
||||||
"exists",
|
|
||||||
"nexist",
|
|
||||||
"nexists",
|
|
||||||
"empty",
|
|
||||||
"emptyset",
|
|
||||||
"isin",
|
|
||||||
"in",
|
|
||||||
"notin",
|
|
||||||
"ni",
|
|
||||||
"nabla",
|
|
||||||
"ang",
|
|
||||||
"angle",
|
|
||||||
"perp",
|
|
||||||
"parallel",
|
|
||||||
"sdot",
|
|
||||||
"cdot",
|
|
||||||
"lceil",
|
|
||||||
"rceil",
|
|
||||||
"lfloor",
|
|
||||||
"rfloor",
|
|
||||||
"lang",
|
|
||||||
"rang",
|
|
||||||
"langle",
|
|
||||||
"rangle",
|
|
||||||
"hbar",
|
|
||||||
"mho",
|
|
||||||
"larr",
|
|
||||||
"leftarrow",
|
|
||||||
"gets",
|
|
||||||
"lArr",
|
|
||||||
"Leftarrow",
|
|
||||||
"uarr",
|
|
||||||
"uparrow",
|
|
||||||
"uArr",
|
|
||||||
"Uparrow",
|
|
||||||
"rarr",
|
|
||||||
"to",
|
|
||||||
"rightarrow",
|
|
||||||
"rArr",
|
|
||||||
"Rightarrow",
|
|
||||||
"darr",
|
|
||||||
"downarrow",
|
|
||||||
"dArr",
|
|
||||||
"Downarrow",
|
|
||||||
"harr",
|
|
||||||
"leftrightarrow",
|
|
||||||
"hArr",
|
|
||||||
"Leftrightarrow",
|
|
||||||
"crarr",
|
|
||||||
"hookleftarrow",
|
|
||||||
"arccos",
|
|
||||||
"arcsin",
|
|
||||||
"arctan",
|
|
||||||
"arg",
|
|
||||||
"cos",
|
|
||||||
"cosh",
|
|
||||||
"cot",
|
|
||||||
"coth",
|
|
||||||
"csc",
|
|
||||||
"deg",
|
|
||||||
"det",
|
|
||||||
"dim",
|
|
||||||
"exp",
|
|
||||||
"gcd",
|
|
||||||
"hom",
|
|
||||||
"inf",
|
|
||||||
"ker",
|
|
||||||
"lg",
|
|
||||||
"lim",
|
|
||||||
"liminf",
|
|
||||||
"limsup",
|
|
||||||
"ln",
|
|
||||||
"log",
|
|
||||||
"max",
|
|
||||||
"min",
|
|
||||||
"Pr",
|
|
||||||
"sec",
|
|
||||||
"sin",
|
|
||||||
"sinh",
|
|
||||||
"sup",
|
|
||||||
"tan",
|
|
||||||
"tanh",
|
|
||||||
"bull",
|
|
||||||
"bullet",
|
|
||||||
"star",
|
|
||||||
"lowast",
|
|
||||||
"ast",
|
|
||||||
"odot",
|
|
||||||
"oplus",
|
|
||||||
"otimes",
|
|
||||||
"check",
|
|
||||||
"checkmark",
|
|
||||||
"para",
|
|
||||||
"ordf",
|
|
||||||
"ordm",
|
|
||||||
"cedil",
|
|
||||||
"oline",
|
|
||||||
"uml",
|
|
||||||
"zwnj",
|
|
||||||
"zwj",
|
|
||||||
"lrm",
|
|
||||||
"rlm",
|
|
||||||
"smiley",
|
|
||||||
"blacksmile",
|
|
||||||
"sad",
|
|
||||||
"frowny",
|
|
||||||
"clubs",
|
|
||||||
"clubsuit",
|
|
||||||
"spades",
|
|
||||||
"spadesuit",
|
|
||||||
"hearts",
|
|
||||||
"heartsuit",
|
|
||||||
"diams",
|
|
||||||
"diamondsuit",
|
|
||||||
"diamond",
|
|
||||||
"Diamond",
|
|
||||||
"loz",
|
|
||||||
"_ ",
|
|
||||||
"_ ",
|
|
||||||
"_ ",
|
|
||||||
"_ ",
|
|
||||||
"_ ",
|
|
||||||
"_ ",
|
|
||||||
"_ ",
|
|
||||||
"_ ",
|
|
||||||
"_ ",
|
|
||||||
"_ ",
|
|
||||||
"_ ",
|
|
||||||
"_ ",
|
|
||||||
"_ ",
|
|
||||||
"_ ",
|
|
||||||
"_ ",
|
|
||||||
"_ ",
|
|
||||||
"_ ",
|
|
||||||
"_ ",
|
|
||||||
"_ ",
|
|
||||||
"_ ",
|
|
||||||
];
|
|
||||||
|
|
||||||
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
|
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
|
||||||
pub fn entity<'r, 's>(
|
pub fn entity<'r, 's>(
|
||||||
context: Context<'r, 's>,
|
context: Context<'r, 's>,
|
||||||
@@ -439,7 +21,10 @@ pub fn entity<'r, 's>(
|
|||||||
) -> Res<OrgSource<'s>, Entity<'s>> {
|
) -> Res<OrgSource<'s>, Entity<'s>> {
|
||||||
let (remaining, _) = tag("\\")(input)?;
|
let (remaining, _) = tag("\\")(input)?;
|
||||||
let (remaining, entity_name) = name(context, remaining)?;
|
let (remaining, entity_name) = name(context, remaining)?;
|
||||||
let (remaining, _) = alt((tag("{}"), peek(recognize(entity_end))))(remaining)?;
|
let (remaining, _) = alt((
|
||||||
|
tag("{}"),
|
||||||
|
peek(recognize(parser_with_context!(entity_end)(context))),
|
||||||
|
))(remaining)?;
|
||||||
let (remaining, _) = space0(remaining)?;
|
let (remaining, _) = space0(remaining)?;
|
||||||
|
|
||||||
let source = get_consumed(input, remaining);
|
let source = get_consumed(input, remaining);
|
||||||
@@ -458,23 +43,14 @@ fn name<'r, 's>(
|
|||||||
input: OrgSource<'s>,
|
input: OrgSource<'s>,
|
||||||
) -> Res<OrgSource<'s>, OrgSource<'s>> {
|
) -> Res<OrgSource<'s>, OrgSource<'s>> {
|
||||||
// TODO: This should be defined by org-entities and optionally org-entities-user
|
// TODO: This should be defined by org-entities and optionally org-entities-user
|
||||||
for entity in ORG_ENTITIES {
|
|
||||||
let result = tag_no_case::<_, _, CustomError<_>>(entity)(input);
|
|
||||||
match result {
|
|
||||||
Ok((remaining, ent)) => {
|
|
||||||
return Ok((remaining, ent));
|
|
||||||
}
|
|
||||||
Err(_) => {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Err(nom::Err::Error(CustomError::MyError(MyError(
|
// TODO: Add the rest of the entities, this is a very incomplete list
|
||||||
"NoEntity".into(),
|
let (remaining, proto) = alt((alt((tag_no_case("delta"), tag_no_case("pi"))),))(input)?;
|
||||||
))))
|
Ok((remaining, proto))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
|
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
|
||||||
fn entity_end<'s>(input: OrgSource<'s>) -> Res<OrgSource<'s>, ()> {
|
fn entity_end<'r, 's>(_context: Context<'r, 's>, input: OrgSource<'s>) -> Res<OrgSource<'s>, ()> {
|
||||||
let (remaining, _) = alt((eof, recognize(satisfy(|c| !c.is_alphabetic()))))(input)?;
|
let (remaining, _) = alt((eof, recognize(satisfy(|c| !c.is_alphabetic()))))(input)?;
|
||||||
|
|
||||||
Ok((remaining, ()))
|
Ok((remaining, ()))
|
||||||
|
|||||||
@@ -5,7 +5,6 @@ use nom::character::complete::space0;
|
|||||||
use nom::combinator::verify;
|
use nom::combinator::verify;
|
||||||
use nom::multi::many_till;
|
use nom::multi::many_till;
|
||||||
|
|
||||||
use super::org_source::BracketDepth;
|
|
||||||
use super::org_source::OrgSource;
|
use super::org_source::OrgSource;
|
||||||
use super::parser_context::ContextElement;
|
use super::parser_context::ContextElement;
|
||||||
use super::Context;
|
use super::Context;
|
||||||
@@ -16,6 +15,7 @@ use crate::parser::exiting::ExitClass;
|
|||||||
use crate::parser::footnote_definition::label;
|
use crate::parser::footnote_definition::label;
|
||||||
use crate::parser::object_parser::standard_set_object;
|
use crate::parser::object_parser::standard_set_object;
|
||||||
use crate::parser::parser_context::ExitMatcherNode;
|
use crate::parser::parser_context::ExitMatcherNode;
|
||||||
|
use crate::parser::parser_context::FootnoteReferenceDefinition;
|
||||||
use crate::parser::parser_with_context::parser_with_context;
|
use crate::parser::parser_with_context::parser_with_context;
|
||||||
use crate::parser::util::exit_matcher_parser;
|
use crate::parser::util::exit_matcher_parser;
|
||||||
use crate::parser::util::get_consumed;
|
use crate::parser::util::get_consumed;
|
||||||
@@ -39,12 +39,18 @@ fn anonymous_footnote<'r, 's>(
|
|||||||
input: OrgSource<'s>,
|
input: OrgSource<'s>,
|
||||||
) -> Res<OrgSource<'s>, FootnoteReference<'s>> {
|
) -> Res<OrgSource<'s>, FootnoteReference<'s>> {
|
||||||
let (remaining, _) = tag_no_case("[fn::")(input)?;
|
let (remaining, _) = tag_no_case("[fn::")(input)?;
|
||||||
let exit_with_depth = footnote_definition_end(remaining.get_bracket_depth());
|
let parser_context = context
|
||||||
let parser_context =
|
.with_additional_node(ContextElement::FootnoteReferenceDefinition(
|
||||||
context.with_additional_node(ContextElement::ExitMatcherNode(ExitMatcherNode {
|
FootnoteReferenceDefinition {
|
||||||
|
position: remaining,
|
||||||
|
depth: 0,
|
||||||
|
},
|
||||||
|
))
|
||||||
|
.with_additional_node(ContextElement::ExitMatcherNode(ExitMatcherNode {
|
||||||
class: ExitClass::Beta,
|
class: ExitClass::Beta,
|
||||||
exit_matcher: &exit_with_depth,
|
exit_matcher: &footnote_definition_end,
|
||||||
}));
|
}));
|
||||||
|
// TODO: I could insert FootnoteReferenceDefinition entries in the context after each matched object to reduce the scanning done for counting brackets which should be more efficient.
|
||||||
let (remaining, (children, _exit_contents)) = verify(
|
let (remaining, (children, _exit_contents)) = verify(
|
||||||
many_till(
|
many_till(
|
||||||
parser_with_context!(standard_set_object)(&parser_context),
|
parser_with_context!(standard_set_object)(&parser_context),
|
||||||
@@ -74,12 +80,18 @@ fn inline_footnote<'r, 's>(
|
|||||||
let (remaining, _) = tag_no_case("[fn:")(input)?;
|
let (remaining, _) = tag_no_case("[fn:")(input)?;
|
||||||
let (remaining, label_contents) = label(remaining)?;
|
let (remaining, label_contents) = label(remaining)?;
|
||||||
let (remaining, _) = tag(":")(remaining)?;
|
let (remaining, _) = tag(":")(remaining)?;
|
||||||
let exit_with_depth = footnote_definition_end(remaining.get_bracket_depth());
|
let parser_context = context
|
||||||
let parser_context =
|
.with_additional_node(ContextElement::FootnoteReferenceDefinition(
|
||||||
context.with_additional_node(ContextElement::ExitMatcherNode(ExitMatcherNode {
|
FootnoteReferenceDefinition {
|
||||||
|
position: remaining,
|
||||||
|
depth: 0,
|
||||||
|
},
|
||||||
|
))
|
||||||
|
.with_additional_node(ContextElement::ExitMatcherNode(ExitMatcherNode {
|
||||||
class: ExitClass::Beta,
|
class: ExitClass::Beta,
|
||||||
exit_matcher: &exit_with_depth,
|
exit_matcher: &footnote_definition_end,
|
||||||
}));
|
}));
|
||||||
|
// TODO: I could insert FootnoteReferenceDefinition entries in the context after each matched object to reduce the scanning done for counting brackets which should be more efficient.
|
||||||
let (remaining, (children, _exit_contents)) = verify(
|
let (remaining, (children, _exit_contents)) = verify(
|
||||||
many_till(
|
many_till(
|
||||||
parser_with_context!(standard_set_object)(&parser_context),
|
parser_with_context!(standard_set_object)(&parser_context),
|
||||||
@@ -121,30 +133,47 @@ fn footnote_reference_only<'r, 's>(
|
|||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn footnote_definition_end(
|
|
||||||
starting_bracket_depth: BracketDepth,
|
|
||||||
) -> impl for<'r, 's> Fn(Context<'r, 's>, OrgSource<'s>) -> Res<OrgSource<'s>, OrgSource<'s>> {
|
|
||||||
move |context: Context, input: OrgSource<'_>| {
|
|
||||||
_footnote_definition_end(context, input, starting_bracket_depth)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
|
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
|
||||||
fn _footnote_definition_end<'r, 's>(
|
fn footnote_definition_end<'r, 's>(
|
||||||
_context: Context<'r, 's>,
|
context: Context<'r, 's>,
|
||||||
input: OrgSource<'s>,
|
input: OrgSource<'s>,
|
||||||
starting_bracket_depth: BracketDepth,
|
|
||||||
) -> Res<OrgSource<'s>, OrgSource<'s>> {
|
) -> Res<OrgSource<'s>, OrgSource<'s>> {
|
||||||
let current_depth = input.get_bracket_depth() - starting_bracket_depth;
|
let context_depth = get_bracket_depth(context)
|
||||||
|
.expect("This function should only be called from inside a footnote definition.");
|
||||||
|
let text_since_context_entry = get_consumed(context_depth.position, input);
|
||||||
|
let mut current_depth = context_depth.depth;
|
||||||
|
for c in Into::<&str>::into(text_since_context_entry).chars() {
|
||||||
|
match c {
|
||||||
|
'[' => {
|
||||||
|
current_depth += 1;
|
||||||
|
}
|
||||||
|
']' if current_depth == 0 => {
|
||||||
|
panic!("Exceeded footnote reference definition bracket depth.")
|
||||||
|
}
|
||||||
|
']' if current_depth > 0 => {
|
||||||
|
current_depth -= 1;
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
if current_depth > 0 {
|
if current_depth > 0 {
|
||||||
// Its impossible for the next character to end the footnote reference definition if we're any amount of brackets deep
|
// Its impossible for the next character to end the footnote reference definition if we're any amount of brackets deep
|
||||||
return Err(nom::Err::Error(CustomError::MyError(MyError(
|
return Err(nom::Err::Error(CustomError::MyError(MyError(
|
||||||
"NoFootnoteReferenceDefinitionEnd".into(),
|
"NoFootnoteReferenceDefinitionEnd".into(),
|
||||||
))));
|
))));
|
||||||
}
|
}
|
||||||
if current_depth < 0 {
|
|
||||||
// This shouldn't be possible because if depth is 0 then a closing bracket should end the footnote definition.
|
|
||||||
unreachable!("Exceeded footnote reference definition bracket depth.")
|
|
||||||
}
|
|
||||||
tag("]")(input)
|
tag("]")(input)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
|
||||||
|
fn get_bracket_depth<'r, 's>(
|
||||||
|
context: Context<'r, 's>,
|
||||||
|
) -> Option<&'r FootnoteReferenceDefinition<'s>> {
|
||||||
|
for node in context.iter() {
|
||||||
|
match node.get_data() {
|
||||||
|
ContextElement::FootnoteReferenceDefinition(depth) => return Some(depth),
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|||||||
@@ -58,15 +58,15 @@ pub fn greater_block<'r, 's>(
|
|||||||
"Cannot nest objects of the same element".into(),
|
"Cannot nest objects of the same element".into(),
|
||||||
))));
|
))));
|
||||||
}
|
}
|
||||||
let exit_with_name = greater_block_end(name.into());
|
|
||||||
let (remaining, parameters) = opt(tuple((space1, parameters)))(remaining)?;
|
let (remaining, parameters) = opt(tuple((space1, parameters)))(remaining)?;
|
||||||
let (remaining, _nl) = line_ending(remaining)?;
|
let (remaining, _nl) = line_ending(remaining)?;
|
||||||
let parser_context = context
|
let parser_context = context
|
||||||
.with_additional_node(ContextElement::ConsumeTrailingWhitespace(true))
|
.with_additional_node(ContextElement::ConsumeTrailingWhitespace(true))
|
||||||
.with_additional_node(ContextElement::Context(context_name))
|
.with_additional_node(ContextElement::Context(context_name))
|
||||||
|
.with_additional_node(ContextElement::GreaterBlock(name.into()))
|
||||||
.with_additional_node(ContextElement::ExitMatcherNode(ExitMatcherNode {
|
.with_additional_node(ContextElement::ExitMatcherNode(ExitMatcherNode {
|
||||||
class: ExitClass::Alpha,
|
class: ExitClass::Alpha,
|
||||||
exit_matcher: &exit_with_name,
|
exit_matcher: &greater_block_end,
|
||||||
}));
|
}));
|
||||||
let parameters = match parameters {
|
let parameters = match parameters {
|
||||||
Some((_ws, parameters)) => Some(parameters),
|
Some((_ws, parameters)) => Some(parameters),
|
||||||
@@ -94,7 +94,7 @@ pub fn greater_block<'r, 's>(
|
|||||||
(remaining, children)
|
(remaining, children)
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
let (remaining, _end) = exit_with_name(&parser_context, remaining)?;
|
let (remaining, _end) = greater_block_end(&parser_context, remaining)?;
|
||||||
|
|
||||||
// Not checking if parent exit matcher is causing exit because the greater_block_end matcher asserts we matched a full greater block
|
// Not checking if parent exit matcher is causing exit because the greater_block_end matcher asserts we matched a full greater block
|
||||||
|
|
||||||
@@ -120,27 +120,31 @@ fn parameters<'s>(input: OrgSource<'s>) -> Res<OrgSource<'s>, OrgSource<'s>> {
|
|||||||
is_not("\r\n")(input)
|
is_not("\r\n")(input)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn greater_block_end<'x>(
|
|
||||||
name: &'x str,
|
|
||||||
) -> impl for<'r, 's> Fn(Context<'r, 's>, OrgSource<'s>) -> Res<OrgSource<'s>, OrgSource<'s>> {
|
|
||||||
// TODO: Can this be done without making an owned copy?
|
|
||||||
let name = name.to_owned();
|
|
||||||
move |context: Context, input: OrgSource<'_>| _greater_block_end(context, input, name.as_str())
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
|
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
|
||||||
fn _greater_block_end<'r, 's, 'x>(
|
fn greater_block_end<'r, 's>(
|
||||||
_context: Context<'r, 's>,
|
context: Context<'r, 's>,
|
||||||
input: OrgSource<'s>,
|
input: OrgSource<'s>,
|
||||||
name: &'x str,
|
|
||||||
) -> Res<OrgSource<'s>, OrgSource<'s>> {
|
) -> Res<OrgSource<'s>, OrgSource<'s>> {
|
||||||
start_of_line(input)?;
|
start_of_line(input)?;
|
||||||
|
let current_name: &str = get_context_greater_block_name(context).ok_or(nom::Err::Error(
|
||||||
|
CustomError::MyError(MyError("Not inside a greater block".into())),
|
||||||
|
))?;
|
||||||
let (remaining, _leading_whitespace) = space0(input)?;
|
let (remaining, _leading_whitespace) = space0(input)?;
|
||||||
let (remaining, (_begin, _name, _ws)) = tuple((
|
let (remaining, (_begin, _name, _ws)) = tuple((
|
||||||
tag_no_case("#+end_"),
|
tag_no_case("#+end_"),
|
||||||
tag_no_case(name),
|
tag_no_case(current_name),
|
||||||
alt((eof, line_ending)),
|
alt((eof, line_ending)),
|
||||||
))(remaining)?;
|
))(remaining)?;
|
||||||
let source = get_consumed(input, remaining);
|
let source = get_consumed(input, remaining);
|
||||||
Ok((remaining, source))
|
Ok((remaining, source))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn get_context_greater_block_name<'r, 's>(context: Context<'r, 's>) -> Option<&'s str> {
|
||||||
|
for thing in context.iter() {
|
||||||
|
match thing.get_data() {
|
||||||
|
ContextElement::GreaterBlock(name) => return Some(name),
|
||||||
|
_ => {}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
use super::element::Element;
|
use super::element::Element;
|
||||||
use super::lesser_element::TableCell;
|
use super::lesser_element::TableCell;
|
||||||
use super::source::Source;
|
use super::source::Source;
|
||||||
use super::Object;
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct PlainList<'s> {
|
pub struct PlainList<'s> {
|
||||||
@@ -14,7 +13,6 @@ pub struct PlainListItem<'s> {
|
|||||||
pub source: &'s str,
|
pub source: &'s str,
|
||||||
pub indentation: usize,
|
pub indentation: usize,
|
||||||
pub bullet: &'s str,
|
pub bullet: &'s str,
|
||||||
pub tag: Vec<Object<'s>>,
|
|
||||||
pub children: Vec<Element<'s>>,
|
pub children: Vec<Element<'s>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -10,13 +10,11 @@ use nom::combinator::recognize;
|
|||||||
use nom::combinator::verify;
|
use nom::combinator::verify;
|
||||||
use nom::multi::many_till;
|
use nom::multi::many_till;
|
||||||
|
|
||||||
use super::org_source::BracketDepth;
|
|
||||||
use super::org_source::OrgSource;
|
use super::org_source::OrgSource;
|
||||||
use super::Context;
|
use super::Context;
|
||||||
use crate::error::CustomError;
|
|
||||||
use crate::error::MyError;
|
|
||||||
use crate::error::Res;
|
use crate::error::Res;
|
||||||
use crate::parser::exiting::ExitClass;
|
use crate::parser::exiting::ExitClass;
|
||||||
|
use crate::parser::parser_context::BabelHeaderBracket;
|
||||||
use crate::parser::parser_context::ContextElement;
|
use crate::parser::parser_context::ContextElement;
|
||||||
use crate::parser::parser_context::ExitMatcherNode;
|
use crate::parser::parser_context::ExitMatcherNode;
|
||||||
use crate::parser::parser_with_context::parser_with_context;
|
use crate::parser::parser_with_context::parser_with_context;
|
||||||
@@ -76,11 +74,14 @@ fn header<'r, 's>(
|
|||||||
) -> Res<OrgSource<'s>, OrgSource<'s>> {
|
) -> Res<OrgSource<'s>, OrgSource<'s>> {
|
||||||
let (remaining, _) = tag("[")(input)?;
|
let (remaining, _) = tag("[")(input)?;
|
||||||
|
|
||||||
let exit_with_depth = header_end(remaining.get_bracket_depth());
|
let parser_context = context
|
||||||
let parser_context =
|
.with_additional_node(ContextElement::BabelHeaderBracket(BabelHeaderBracket {
|
||||||
context.with_additional_node(ContextElement::ExitMatcherNode(ExitMatcherNode {
|
position: remaining,
|
||||||
|
depth: 0,
|
||||||
|
}))
|
||||||
|
.with_additional_node(ContextElement::ExitMatcherNode(ExitMatcherNode {
|
||||||
class: ExitClass::Gamma,
|
class: ExitClass::Gamma,
|
||||||
exit_matcher: &exit_with_depth,
|
exit_matcher: &header_end,
|
||||||
}));
|
}));
|
||||||
|
|
||||||
let (remaining, name) = recognize(many_till(
|
let (remaining, name) = recognize(many_till(
|
||||||
@@ -91,30 +92,28 @@ fn header<'r, 's>(
|
|||||||
Ok((remaining, name))
|
Ok((remaining, name))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn header_end(
|
|
||||||
starting_bracket_depth: BracketDepth,
|
|
||||||
) -> impl for<'r, 's> Fn(Context<'r, 's>, OrgSource<'s>) -> Res<OrgSource<'s>, OrgSource<'s>> {
|
|
||||||
move |context: Context, input: OrgSource<'_>| {
|
|
||||||
_header_end(context, input, starting_bracket_depth)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
|
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
|
||||||
fn _header_end<'r, 's>(
|
fn header_end<'r, 's>(
|
||||||
_context: Context<'r, 's>,
|
context: Context<'r, 's>,
|
||||||
input: OrgSource<'s>,
|
input: OrgSource<'s>,
|
||||||
starting_bracket_depth: BracketDepth,
|
|
||||||
) -> Res<OrgSource<'s>, OrgSource<'s>> {
|
) -> Res<OrgSource<'s>, OrgSource<'s>> {
|
||||||
let current_depth = input.get_bracket_depth() - starting_bracket_depth;
|
let context_depth = get_bracket_depth(context)
|
||||||
if current_depth > 0 {
|
.expect("This function should only be called from inside an inline babel call header.");
|
||||||
// Its impossible for the next character to end the header if we're any amount of bracket deep
|
let text_since_context_entry = get_consumed(context_depth.position, input);
|
||||||
return Err(nom::Err::Error(CustomError::MyError(MyError(
|
let mut current_depth = context_depth.depth;
|
||||||
"NoHeaderEnd".into(),
|
for c in Into::<&str>::into(text_since_context_entry).chars() {
|
||||||
))));
|
match c {
|
||||||
}
|
'(' => {
|
||||||
if current_depth < 0 {
|
current_depth += 1;
|
||||||
// This shouldn't be possible because if depth is 0 then a closing bracket should end the header.
|
}
|
||||||
unreachable!("Exceeded header bracket depth.")
|
')' if current_depth == 0 => {
|
||||||
|
panic!("Exceeded inline babel call header bracket depth.")
|
||||||
|
}
|
||||||
|
')' if current_depth > 0 => {
|
||||||
|
current_depth -= 1;
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
alt((tag("]"), line_ending))(input)
|
alt((tag("]"), line_ending))(input)
|
||||||
}
|
}
|
||||||
@@ -126,11 +125,14 @@ fn argument<'r, 's>(
|
|||||||
) -> Res<OrgSource<'s>, OrgSource<'s>> {
|
) -> Res<OrgSource<'s>, OrgSource<'s>> {
|
||||||
let (remaining, _) = tag("(")(input)?;
|
let (remaining, _) = tag("(")(input)?;
|
||||||
|
|
||||||
let exit_with_depth = argument_end(remaining.get_parenthesis_depth());
|
let parser_context = context
|
||||||
let parser_context =
|
.with_additional_node(ContextElement::BabelHeaderBracket(BabelHeaderBracket {
|
||||||
context.with_additional_node(ContextElement::ExitMatcherNode(ExitMatcherNode {
|
position: remaining,
|
||||||
|
depth: 0,
|
||||||
|
}))
|
||||||
|
.with_additional_node(ContextElement::ExitMatcherNode(ExitMatcherNode {
|
||||||
class: ExitClass::Gamma,
|
class: ExitClass::Gamma,
|
||||||
exit_matcher: &exit_with_depth,
|
exit_matcher: &argument_end,
|
||||||
}));
|
}));
|
||||||
|
|
||||||
let (remaining, name) = recognize(many_till(
|
let (remaining, name) = recognize(many_till(
|
||||||
@@ -141,30 +143,39 @@ fn argument<'r, 's>(
|
|||||||
Ok((remaining, name))
|
Ok((remaining, name))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn argument_end(
|
|
||||||
starting_parenthesis_depth: BracketDepth,
|
|
||||||
) -> impl for<'r, 's> Fn(Context<'r, 's>, OrgSource<'s>) -> Res<OrgSource<'s>, OrgSource<'s>> {
|
|
||||||
move |context: Context, input: OrgSource<'_>| {
|
|
||||||
_argument_end(context, input, starting_parenthesis_depth)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
|
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
|
||||||
fn _argument_end<'r, 's>(
|
fn argument_end<'r, 's>(
|
||||||
_context: Context<'r, 's>,
|
context: Context<'r, 's>,
|
||||||
input: OrgSource<'s>,
|
input: OrgSource<'s>,
|
||||||
starting_parenthesis_depth: BracketDepth,
|
|
||||||
) -> Res<OrgSource<'s>, OrgSource<'s>> {
|
) -> Res<OrgSource<'s>, OrgSource<'s>> {
|
||||||
let current_depth = input.get_parenthesis_depth() - starting_parenthesis_depth;
|
let context_depth = get_bracket_depth(context)
|
||||||
if current_depth > 0 {
|
.expect("This function should only be called from inside an inline babel call argument.");
|
||||||
// Its impossible for the next character to end the argument if we're any amount of parenthesis deep
|
let text_since_context_entry = get_consumed(context_depth.position, input);
|
||||||
return Err(nom::Err::Error(CustomError::MyError(MyError(
|
let mut current_depth = context_depth.depth;
|
||||||
"NoArgumentEnd".into(),
|
for c in Into::<&str>::into(text_since_context_entry).chars() {
|
||||||
))));
|
match c {
|
||||||
}
|
'[' => {
|
||||||
if current_depth < 0 {
|
current_depth += 1;
|
||||||
// This shouldn't be possible because if depth is 0 then a closing parenthesis should end the argument.
|
}
|
||||||
unreachable!("Exceeded argument parenthesis depth.")
|
']' if current_depth == 0 => {
|
||||||
|
panic!("Exceeded inline babel call argument bracket depth.")
|
||||||
|
}
|
||||||
|
']' if current_depth > 0 => {
|
||||||
|
current_depth -= 1;
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
alt((tag(")"), line_ending))(input)
|
alt((tag(")"), line_ending))(input)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
|
||||||
|
pub fn get_bracket_depth<'r, 's>(context: Context<'r, 's>) -> Option<&'r BabelHeaderBracket<'s>> {
|
||||||
|
for node in context.iter() {
|
||||||
|
match node.get_data() {
|
||||||
|
ContextElement::BabelHeaderBracket(depth) => return Some(depth),
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
use nom::branch::alt;
|
|
||||||
use nom::bytes::complete::tag;
|
use nom::bytes::complete::tag;
|
||||||
use nom::bytes::complete::tag_no_case;
|
use nom::bytes::complete::tag_no_case;
|
||||||
use nom::character::complete::anychar;
|
use nom::character::complete::anychar;
|
||||||
@@ -12,15 +11,14 @@ use nom::multi::many_till;
|
|||||||
#[cfg(feature = "tracing")]
|
#[cfg(feature = "tracing")]
|
||||||
use tracing::span;
|
use tracing::span;
|
||||||
|
|
||||||
use super::org_source::BracketDepth;
|
|
||||||
use super::org_source::OrgSource;
|
use super::org_source::OrgSource;
|
||||||
use super::Context;
|
use super::Context;
|
||||||
use crate::error::CustomError;
|
use crate::error::CustomError;
|
||||||
use crate::error::MyError;
|
|
||||||
use crate::error::Res;
|
use crate::error::Res;
|
||||||
use crate::parser::exiting::ExitClass;
|
use crate::parser::exiting::ExitClass;
|
||||||
use crate::parser::parser_context::ContextElement;
|
use crate::parser::parser_context::ContextElement;
|
||||||
use crate::parser::parser_context::ExitMatcherNode;
|
use crate::parser::parser_context::ExitMatcherNode;
|
||||||
|
use crate::parser::parser_context::InlineSourceBlockBracket;
|
||||||
use crate::parser::parser_with_context::parser_with_context;
|
use crate::parser::parser_with_context::parser_with_context;
|
||||||
use crate::parser::util::exit_matcher_parser;
|
use crate::parser::util::exit_matcher_parser;
|
||||||
use crate::parser::util::get_consumed;
|
use crate::parser::util::get_consumed;
|
||||||
@@ -77,11 +75,16 @@ fn header<'r, 's>(
|
|||||||
) -> Res<OrgSource<'s>, OrgSource<'s>> {
|
) -> Res<OrgSource<'s>, OrgSource<'s>> {
|
||||||
let (remaining, _) = tag("[")(input)?;
|
let (remaining, _) = tag("[")(input)?;
|
||||||
|
|
||||||
let exit_with_depth = header_end(remaining.get_bracket_depth());
|
let parser_context = context
|
||||||
let parser_context =
|
.with_additional_node(ContextElement::InlineSourceBlockBracket(
|
||||||
context.with_additional_node(ContextElement::ExitMatcherNode(ExitMatcherNode {
|
InlineSourceBlockBracket {
|
||||||
|
position: remaining,
|
||||||
|
depth: 0,
|
||||||
|
},
|
||||||
|
))
|
||||||
|
.with_additional_node(ContextElement::ExitMatcherNode(ExitMatcherNode {
|
||||||
class: ExitClass::Beta,
|
class: ExitClass::Beta,
|
||||||
exit_matcher: &exit_with_depth,
|
exit_matcher: &header_end,
|
||||||
}));
|
}));
|
||||||
|
|
||||||
let (remaining, header_contents) = recognize(many_till(
|
let (remaining, header_contents) = recognize(many_till(
|
||||||
@@ -92,32 +95,37 @@ fn header<'r, 's>(
|
|||||||
Ok((remaining, header_contents))
|
Ok((remaining, header_contents))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn header_end(
|
|
||||||
starting_bracket_depth: BracketDepth,
|
|
||||||
) -> impl for<'r, 's> Fn(Context<'r, 's>, OrgSource<'s>) -> Res<OrgSource<'s>, OrgSource<'s>> {
|
|
||||||
move |context: Context, input: OrgSource<'_>| {
|
|
||||||
_header_end(context, input, starting_bracket_depth)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
|
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
|
||||||
fn _header_end<'r, 's>(
|
fn header_end<'r, 's>(
|
||||||
_context: Context<'r, 's>,
|
context: Context<'r, 's>,
|
||||||
input: OrgSource<'s>,
|
input: OrgSource<'s>,
|
||||||
starting_bracket_depth: BracketDepth,
|
|
||||||
) -> Res<OrgSource<'s>, OrgSource<'s>> {
|
) -> Res<OrgSource<'s>, OrgSource<'s>> {
|
||||||
let current_depth = input.get_bracket_depth() - starting_bracket_depth;
|
let context_depth = get_bracket_depth(context)
|
||||||
if current_depth > 0 {
|
.expect("This function should only be called from inside an inline source block header.");
|
||||||
// Its impossible for the next character to end the header if we're any amount of bracket deep
|
let text_since_context_entry = get_consumed(context_depth.position, input);
|
||||||
return Err(nom::Err::Error(CustomError::MyError(MyError(
|
let mut current_depth = context_depth.depth;
|
||||||
"NoHeaderEnd".into(),
|
for c in Into::<&str>::into(text_since_context_entry).chars() {
|
||||||
))));
|
match c {
|
||||||
|
'[' => {
|
||||||
|
current_depth += 1;
|
||||||
|
}
|
||||||
|
']' if current_depth == 0 => {
|
||||||
|
panic!("Exceeded inline source block header bracket depth.")
|
||||||
|
}
|
||||||
|
']' if current_depth > 0 => {
|
||||||
|
current_depth -= 1;
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if current_depth < 0 {
|
if current_depth == 0 {
|
||||||
// This shouldn't be possible because if depth is 0 then a closing bracket should end the header.
|
let close_bracket = tag::<&str, OrgSource<'_>, CustomError<OrgSource<'_>>>("]")(input);
|
||||||
unreachable!("Exceeded header bracket depth.")
|
if close_bracket.is_ok() {
|
||||||
|
return close_bracket;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
alt((tag("]"), line_ending))(input)
|
|
||||||
|
line_ending(input)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
|
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
|
||||||
@@ -127,11 +135,16 @@ fn body<'r, 's>(
|
|||||||
) -> Res<OrgSource<'s>, OrgSource<'s>> {
|
) -> Res<OrgSource<'s>, OrgSource<'s>> {
|
||||||
let (remaining, _) = tag("{")(input)?;
|
let (remaining, _) = tag("{")(input)?;
|
||||||
|
|
||||||
let exit_with_depth = body_end(remaining.get_brace_depth());
|
let parser_context = context
|
||||||
let parser_context =
|
.with_additional_node(ContextElement::InlineSourceBlockBracket(
|
||||||
context.with_additional_node(ContextElement::ExitMatcherNode(ExitMatcherNode {
|
InlineSourceBlockBracket {
|
||||||
|
position: remaining,
|
||||||
|
depth: 0,
|
||||||
|
},
|
||||||
|
))
|
||||||
|
.with_additional_node(ContextElement::ExitMatcherNode(ExitMatcherNode {
|
||||||
class: ExitClass::Beta,
|
class: ExitClass::Beta,
|
||||||
exit_matcher: &exit_with_depth,
|
exit_matcher: &body_end,
|
||||||
}));
|
}));
|
||||||
|
|
||||||
let (remaining, body_contents) = recognize(many_till(
|
let (remaining, body_contents) = recognize(many_till(
|
||||||
@@ -152,28 +165,60 @@ fn body<'r, 's>(
|
|||||||
Ok((remaining, body_contents))
|
Ok((remaining, body_contents))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn body_end(
|
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
|
||||||
starting_brace_depth: BracketDepth,
|
fn body_end<'r, 's>(
|
||||||
) -> impl for<'r, 's> Fn(Context<'r, 's>, OrgSource<'s>) -> Res<OrgSource<'s>, OrgSource<'s>> {
|
context: Context<'r, 's>,
|
||||||
move |context: Context, input: OrgSource<'_>| _body_end(context, input, starting_brace_depth)
|
input: OrgSource<'s>,
|
||||||
|
) -> Res<OrgSource<'s>, OrgSource<'s>> {
|
||||||
|
let context_depth = get_bracket_depth(context)
|
||||||
|
.expect("This function should only be called from inside an inline source block body.");
|
||||||
|
let text_since_context_entry = get_consumed(context_depth.position, input);
|
||||||
|
let mut current_depth = context_depth.depth;
|
||||||
|
for c in Into::<&str>::into(text_since_context_entry).chars() {
|
||||||
|
match c {
|
||||||
|
'{' => {
|
||||||
|
current_depth += 1;
|
||||||
|
}
|
||||||
|
'}' if current_depth == 0 => {
|
||||||
|
panic!("Exceeded inline source block body bracket depth.")
|
||||||
|
}
|
||||||
|
'}' if current_depth > 0 => {
|
||||||
|
current_depth -= 1;
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
{
|
||||||
|
#[cfg(feature = "tracing")]
|
||||||
|
let span = span!(
|
||||||
|
tracing::Level::DEBUG,
|
||||||
|
"inside end body",
|
||||||
|
remaining = Into::<&str>::into(input),
|
||||||
|
current_depth = current_depth
|
||||||
|
);
|
||||||
|
#[cfg(feature = "tracing")]
|
||||||
|
let _enter = span.enter();
|
||||||
|
|
||||||
|
if current_depth == 0 {
|
||||||
|
let close_bracket = tag::<&str, OrgSource<'_>, CustomError<OrgSource<'_>>>("}")(input);
|
||||||
|
if close_bracket.is_ok() {
|
||||||
|
return close_bracket;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
line_ending(input)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
|
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
|
||||||
fn _body_end<'r, 's>(
|
pub fn get_bracket_depth<'r, 's>(
|
||||||
_context: Context<'r, 's>,
|
context: Context<'r, 's>,
|
||||||
input: OrgSource<'s>,
|
) -> Option<&'r InlineSourceBlockBracket<'s>> {
|
||||||
starting_brace_depth: BracketDepth,
|
for node in context.iter() {
|
||||||
) -> Res<OrgSource<'s>, OrgSource<'s>> {
|
match node.get_data() {
|
||||||
let current_depth = input.get_brace_depth() - starting_brace_depth;
|
ContextElement::InlineSourceBlockBracket(depth) => return Some(depth),
|
||||||
if current_depth > 0 {
|
_ => {}
|
||||||
// Its impossible for the next character to end the body if we're any amount of brace deep
|
}
|
||||||
return Err(nom::Err::Error(CustomError::MyError(MyError(
|
|
||||||
"NoBodyEnd".into(),
|
|
||||||
))));
|
|
||||||
}
|
}
|
||||||
if current_depth < 0 {
|
None
|
||||||
// This shouldn't be possible because if depth is 0 then a closing brace should end the body.
|
|
||||||
unreachable!("Exceeded body brace depth.")
|
|
||||||
}
|
|
||||||
alt((tag("}"), line_ending))(input)
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,8 +2,6 @@ use nom::branch::alt;
|
|||||||
use nom::bytes::complete::is_not;
|
use nom::bytes::complete::is_not;
|
||||||
use nom::bytes::complete::tag;
|
use nom::bytes::complete::tag;
|
||||||
use nom::bytes::complete::tag_no_case;
|
use nom::bytes::complete::tag_no_case;
|
||||||
use nom::bytes::complete::take_while1;
|
|
||||||
use nom::character::complete::anychar;
|
|
||||||
use nom::character::complete::line_ending;
|
use nom::character::complete::line_ending;
|
||||||
use nom::character::complete::space0;
|
use nom::character::complete::space0;
|
||||||
use nom::character::complete::space1;
|
use nom::character::complete::space1;
|
||||||
@@ -11,24 +9,14 @@ use nom::combinator::eof;
|
|||||||
use nom::combinator::not;
|
use nom::combinator::not;
|
||||||
use nom::combinator::peek;
|
use nom::combinator::peek;
|
||||||
use nom::combinator::recognize;
|
use nom::combinator::recognize;
|
||||||
use nom::multi::many_till;
|
|
||||||
use nom::sequence::tuple;
|
use nom::sequence::tuple;
|
||||||
|
|
||||||
use super::org_source::BracketDepth;
|
|
||||||
use super::org_source::OrgSource;
|
use super::org_source::OrgSource;
|
||||||
use super::Context;
|
use super::Context;
|
||||||
use crate::error::CustomError;
|
|
||||||
use crate::error::MyError;
|
|
||||||
use crate::error::Res;
|
use crate::error::Res;
|
||||||
use crate::parser::util::start_of_line;
|
use crate::parser::util::start_of_line;
|
||||||
use crate::parser::Keyword;
|
use crate::parser::Keyword;
|
||||||
|
|
||||||
const ORG_ELEMENT_AFFILIATED_KEYWORDS: [&'static str; 13] = [
|
|
||||||
"caption", "data", "header", "headers", "label", "name", "plot", "resname", "result",
|
|
||||||
"results", "source", "srcname", "tblname",
|
|
||||||
];
|
|
||||||
const ORG_ELEMENT_DUAL_KEYWORDS: [&'static str; 2] = ["caption", "results"];
|
|
||||||
|
|
||||||
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
|
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
|
||||||
pub fn keyword<'r, 's>(
|
pub fn keyword<'r, 's>(
|
||||||
_context: Context<'r, 's>,
|
_context: Context<'r, 's>,
|
||||||
@@ -53,111 +41,3 @@ pub fn keyword<'r, 's>(
|
|||||||
},
|
},
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
|
|
||||||
pub fn affiliated_keyword<'r, 's>(
|
|
||||||
_context: Context<'r, 's>,
|
|
||||||
input: OrgSource<'s>,
|
|
||||||
) -> Res<OrgSource<'s>, Keyword<'s>> {
|
|
||||||
start_of_line(input)?;
|
|
||||||
|
|
||||||
// TODO: When key is a member of org-element-parsed-keywords, value can contain the standard set objects, excluding footnote references.
|
|
||||||
let (remaining, rule) = recognize(tuple((
|
|
||||||
space0,
|
|
||||||
tag("#+"),
|
|
||||||
affiliated_key,
|
|
||||||
tag(":"),
|
|
||||||
alt((recognize(tuple((space1, is_not("\r\n")))), space0)),
|
|
||||||
alt((line_ending, eof)),
|
|
||||||
)))(input)?;
|
|
||||||
Ok((
|
|
||||||
remaining,
|
|
||||||
Keyword {
|
|
||||||
source: rule.into(),
|
|
||||||
},
|
|
||||||
))
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
|
|
||||||
fn affiliated_key<'s>(input: OrgSource<'s>) -> Res<OrgSource<'s>, OrgSource<'s>> {
|
|
||||||
alt((
|
|
||||||
recognize(tuple((dual_affiliated_key, tag("["), optval, tag("]")))),
|
|
||||||
plain_affiliated_key,
|
|
||||||
export_keyword,
|
|
||||||
))(input)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
|
|
||||||
fn plain_affiliated_key<'s>(input: OrgSource<'s>) -> Res<OrgSource<'s>, OrgSource<'s>> {
|
|
||||||
for keyword in ORG_ELEMENT_AFFILIATED_KEYWORDS {
|
|
||||||
let result = tag_no_case::<_, _, CustomError<_>>(keyword)(input);
|
|
||||||
match result {
|
|
||||||
Ok((remaining, ent)) => {
|
|
||||||
return Ok((remaining, ent));
|
|
||||||
}
|
|
||||||
Err(_) => {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Err(nom::Err::Error(CustomError::MyError(MyError(
|
|
||||||
"NoKeywordKey".into(),
|
|
||||||
))))
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
|
|
||||||
fn dual_affiliated_key<'s>(input: OrgSource<'s>) -> Res<OrgSource<'s>, OrgSource<'s>> {
|
|
||||||
for keyword in ORG_ELEMENT_DUAL_KEYWORDS {
|
|
||||||
let result = tag_no_case::<_, _, CustomError<_>>(keyword)(input);
|
|
||||||
match result {
|
|
||||||
Ok((remaining, ent)) => {
|
|
||||||
return Ok((remaining, ent));
|
|
||||||
}
|
|
||||||
Err(_) => {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Err(nom::Err::Error(CustomError::MyError(MyError(
|
|
||||||
"NoKeywordKey".into(),
|
|
||||||
))))
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
|
|
||||||
fn optval<'s>(input: OrgSource<'s>) -> Res<OrgSource<'s>, OrgSource<'s>> {
|
|
||||||
recognize(many_till(
|
|
||||||
anychar,
|
|
||||||
peek(optval_end(input.get_bracket_depth())),
|
|
||||||
))(input)
|
|
||||||
}
|
|
||||||
|
|
||||||
const fn optval_end(
|
|
||||||
starting_bracket_depth: BracketDepth,
|
|
||||||
) -> impl for<'s> Fn(OrgSource<'s>) -> Res<OrgSource<'s>, OrgSource<'s>> {
|
|
||||||
move |input: OrgSource<'_>| _optval_end(input, starting_bracket_depth)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
|
|
||||||
fn _optval_end<'s>(
|
|
||||||
input: OrgSource<'s>,
|
|
||||||
starting_bracket_depth: BracketDepth,
|
|
||||||
) -> Res<OrgSource<'s>, OrgSource<'s>> {
|
|
||||||
let current_depth = input.get_bracket_depth() - starting_bracket_depth;
|
|
||||||
if current_depth < 0 {
|
|
||||||
// This shouldn't be possible because if depth is 0 then a closing bracket should end the opval.
|
|
||||||
unreachable!("Exceeded optval bracket depth.")
|
|
||||||
}
|
|
||||||
if current_depth == 0 {
|
|
||||||
let close_bracket = tag::<_, _, CustomError<_>>("]")(input);
|
|
||||||
if close_bracket.is_ok() {
|
|
||||||
return close_bracket;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
tag("\n")(input)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
|
|
||||||
fn export_keyword<'s>(input: OrgSource<'s>) -> Res<OrgSource<'s>, OrgSource<'s>> {
|
|
||||||
recognize(tuple((
|
|
||||||
tag_no_case("attr_"),
|
|
||||||
take_while1(|c: char| c.is_alphanumeric() || "-_".contains(c)),
|
|
||||||
)))(input)
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -202,13 +202,17 @@ fn bordered_dollar_fragment<'r, 's>(
|
|||||||
// TODO: I'm assuming I should be peeking at the borders but the documentation is not clear. Test to figure out.
|
// TODO: I'm assuming I should be peeking at the borders but the documentation is not clear. Test to figure out.
|
||||||
let (_, _) = peek(parser_with_context!(open_border)(context))(remaining)?;
|
let (_, _) = peek(parser_with_context!(open_border)(context))(remaining)?;
|
||||||
|
|
||||||
let (remaining, _) = recognize(many_till(
|
// TODO: As an optimization it would be nice to exit early upon hitting the 3rd line break
|
||||||
anychar,
|
let (remaining, _) = verify(
|
||||||
peek(alt((
|
recognize(many_till(
|
||||||
parser_with_context!(exit_matcher_parser)(context),
|
anychar,
|
||||||
tag("$"),
|
peek(alt((
|
||||||
))),
|
parser_with_context!(exit_matcher_parser)(context),
|
||||||
))(remaining)?;
|
tag("$"),
|
||||||
|
))),
|
||||||
|
)),
|
||||||
|
|body: &OrgSource<'_>| Into::<&str>::into(body).lines().take(4).count() <= 3,
|
||||||
|
)(remaining)?;
|
||||||
|
|
||||||
let (_, _) = peek(parser_with_context!(close_border)(context))(remaining)?;
|
let (_, _) = peek(parser_with_context!(close_border)(context))(remaining)?;
|
||||||
let (remaining, _) = tag("$")(remaining)?;
|
let (remaining, _) = tag("$")(remaining)?;
|
||||||
|
|||||||
@@ -374,9 +374,3 @@ impl<'s> Source<'s> for Timestamp<'s> {
|
|||||||
self.source
|
self.source
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'s> Source<'s> for PlainText<'s> {
|
|
||||||
fn get_source(&'s self) -> &'s str {
|
|
||||||
self.source
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -33,7 +33,7 @@ pub fn standard_set_object<'r, 's>(
|
|||||||
context: Context<'r, 's>,
|
context: Context<'r, 's>,
|
||||||
input: OrgSource<'s>,
|
input: OrgSource<'s>,
|
||||||
) -> Res<OrgSource<'s>, Object<'s>> {
|
) -> Res<OrgSource<'s>, Object<'s>> {
|
||||||
let (remaining, object) = alt((
|
alt((
|
||||||
map(parser_with_context!(timestamp)(context), Object::Timestamp),
|
map(parser_with_context!(timestamp)(context), Object::Timestamp),
|
||||||
map(parser_with_context!(subscript)(context), Object::Subscript),
|
map(parser_with_context!(subscript)(context), Object::Subscript),
|
||||||
map(
|
map(
|
||||||
@@ -82,8 +82,7 @@ pub fn standard_set_object<'r, 's>(
|
|||||||
map(parser_with_context!(angle_link)(context), Object::AngleLink),
|
map(parser_with_context!(angle_link)(context), Object::AngleLink),
|
||||||
map(parser_with_context!(org_macro)(context), Object::OrgMacro),
|
map(parser_with_context!(org_macro)(context), Object::OrgMacro),
|
||||||
map(parser_with_context!(plain_text)(context), Object::PlainText),
|
map(parser_with_context!(plain_text)(context), Object::PlainText),
|
||||||
))(input)?;
|
))(input)
|
||||||
Ok((remaining, object))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
|
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
|
||||||
@@ -91,7 +90,7 @@ pub fn minimal_set_object<'r, 's>(
|
|||||||
context: Context<'r, 's>,
|
context: Context<'r, 's>,
|
||||||
input: OrgSource<'s>,
|
input: OrgSource<'s>,
|
||||||
) -> Res<OrgSource<'s>, Object<'s>> {
|
) -> Res<OrgSource<'s>, Object<'s>> {
|
||||||
let (remaining, object) = alt((
|
alt((
|
||||||
map(parser_with_context!(subscript)(context), Object::Subscript),
|
map(parser_with_context!(subscript)(context), Object::Subscript),
|
||||||
map(
|
map(
|
||||||
parser_with_context!(superscript)(context),
|
parser_with_context!(superscript)(context),
|
||||||
@@ -104,8 +103,7 @@ pub fn minimal_set_object<'r, 's>(
|
|||||||
),
|
),
|
||||||
parser_with_context!(text_markup)(context),
|
parser_with_context!(text_markup)(context),
|
||||||
map(parser_with_context!(plain_text)(context), Object::PlainText),
|
map(parser_with_context!(plain_text)(context), Object::PlainText),
|
||||||
))(input)?;
|
))(input)
|
||||||
Ok((remaining, object))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
|
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
|
||||||
@@ -113,7 +111,7 @@ pub fn any_object_except_plain_text<'r, 's>(
|
|||||||
context: Context<'r, 's>,
|
context: Context<'r, 's>,
|
||||||
input: OrgSource<'s>,
|
input: OrgSource<'s>,
|
||||||
) -> Res<OrgSource<'s>, Object<'s>> {
|
) -> Res<OrgSource<'s>, Object<'s>> {
|
||||||
let (remaining, object) = alt((
|
alt((
|
||||||
map(parser_with_context!(timestamp)(context), Object::Timestamp),
|
map(parser_with_context!(timestamp)(context), Object::Timestamp),
|
||||||
map(parser_with_context!(subscript)(context), Object::Subscript),
|
map(parser_with_context!(subscript)(context), Object::Subscript),
|
||||||
map(
|
map(
|
||||||
@@ -161,8 +159,7 @@ pub fn any_object_except_plain_text<'r, 's>(
|
|||||||
map(parser_with_context!(plain_link)(context), Object::PlainLink),
|
map(parser_with_context!(plain_link)(context), Object::PlainLink),
|
||||||
map(parser_with_context!(angle_link)(context), Object::AngleLink),
|
map(parser_with_context!(angle_link)(context), Object::AngleLink),
|
||||||
map(parser_with_context!(org_macro)(context), Object::OrgMacro),
|
map(parser_with_context!(org_macro)(context), Object::OrgMacro),
|
||||||
))(input)?;
|
))(input)
|
||||||
Ok((remaining, object))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
|
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
|
||||||
@@ -171,7 +168,7 @@ pub fn regular_link_description_object_set<'r, 's>(
|
|||||||
input: OrgSource<'s>,
|
input: OrgSource<'s>,
|
||||||
) -> Res<OrgSource<'s>, Object<'s>> {
|
) -> Res<OrgSource<'s>, Object<'s>> {
|
||||||
// TODO: It can also contain another link, but only when it is a plain or angle link. It can contain square brackets, but not ]]
|
// TODO: It can also contain another link, but only when it is a plain or angle link. It can contain square brackets, but not ]]
|
||||||
let (remaining, object) = alt((
|
alt((
|
||||||
map(
|
map(
|
||||||
parser_with_context!(export_snippet)(context),
|
parser_with_context!(export_snippet)(context),
|
||||||
Object::ExportSnippet,
|
Object::ExportSnippet,
|
||||||
@@ -190,40 +187,5 @@ pub fn regular_link_description_object_set<'r, 's>(
|
|||||||
),
|
),
|
||||||
map(parser_with_context!(org_macro)(context), Object::OrgMacro),
|
map(parser_with_context!(org_macro)(context), Object::OrgMacro),
|
||||||
parser_with_context!(minimal_set_object)(context),
|
parser_with_context!(minimal_set_object)(context),
|
||||||
))(input)?;
|
))(input)
|
||||||
Ok((remaining, object))
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
|
|
||||||
pub fn table_cell_set_object<'r, 's>(
|
|
||||||
context: Context<'r, 's>,
|
|
||||||
input: OrgSource<'s>,
|
|
||||||
) -> Res<OrgSource<'s>, Object<'s>> {
|
|
||||||
let (remaining, object) = alt((
|
|
||||||
map(parser_with_context!(citation)(context), Object::Citation),
|
|
||||||
map(
|
|
||||||
parser_with_context!(export_snippet)(context),
|
|
||||||
Object::ExportSnippet,
|
|
||||||
),
|
|
||||||
map(
|
|
||||||
parser_with_context!(footnote_reference)(context),
|
|
||||||
Object::FootnoteReference,
|
|
||||||
),
|
|
||||||
map(parser_with_context!(radio_link)(context), Object::RadioLink),
|
|
||||||
map(
|
|
||||||
parser_with_context!(regular_link)(context),
|
|
||||||
Object::RegularLink,
|
|
||||||
),
|
|
||||||
map(parser_with_context!(plain_link)(context), Object::PlainLink),
|
|
||||||
map(parser_with_context!(angle_link)(context), Object::AngleLink),
|
|
||||||
map(parser_with_context!(org_macro)(context), Object::OrgMacro),
|
|
||||||
map(
|
|
||||||
parser_with_context!(radio_target)(context),
|
|
||||||
Object::RadioTarget,
|
|
||||||
),
|
|
||||||
map(parser_with_context!(target)(context), Object::Target),
|
|
||||||
map(parser_with_context!(timestamp)(context), Object::Timestamp),
|
|
||||||
parser_with_context!(minimal_set_object)(context),
|
|
||||||
))(input)?;
|
|
||||||
Ok((remaining, object))
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
use nom::bytes::complete::tag;
|
use nom::bytes::complete::tag;
|
||||||
use nom::character::complete::anychar;
|
use nom::character::complete::anychar;
|
||||||
use nom::character::complete::space0;
|
|
||||||
use nom::combinator::not;
|
use nom::combinator::not;
|
||||||
use nom::combinator::opt;
|
use nom::combinator::opt;
|
||||||
use nom::combinator::peek;
|
use nom::combinator::peek;
|
||||||
@@ -25,7 +24,6 @@ pub fn org_macro<'r, 's>(
|
|||||||
let (remaining, macro_name) = org_macro_name(context, remaining)?;
|
let (remaining, macro_name) = org_macro_name(context, remaining)?;
|
||||||
let (remaining, macro_args) = opt(parser_with_context!(org_macro_args)(context))(remaining)?;
|
let (remaining, macro_args) = opt(parser_with_context!(org_macro_args)(context))(remaining)?;
|
||||||
let (remaining, _) = tag("}}}")(remaining)?;
|
let (remaining, _) = tag("}}}")(remaining)?;
|
||||||
let (remaining, _trailing_whitespace) = space0(remaining)?;
|
|
||||||
|
|
||||||
let source = get_consumed(input, remaining);
|
let source = get_consumed(input, remaining);
|
||||||
Ok((
|
Ok((
|
||||||
|
|||||||
@@ -11,28 +11,15 @@ use nom::Slice;
|
|||||||
use crate::error::CustomError;
|
use crate::error::CustomError;
|
||||||
use crate::error::MyError;
|
use crate::error::MyError;
|
||||||
|
|
||||||
pub type BracketDepth = i16;
|
#[derive(Debug, Copy, Clone)]
|
||||||
|
|
||||||
#[derive(Copy, Clone)]
|
|
||||||
pub struct OrgSource<'s> {
|
pub struct OrgSource<'s> {
|
||||||
full_source: &'s str,
|
full_source: &'s str,
|
||||||
start: usize,
|
start: usize,
|
||||||
end: usize, // exclusive
|
end: usize, // exclusive
|
||||||
start_of_line: usize,
|
start_of_line: usize,
|
||||||
bracket_depth: BracketDepth, // []
|
|
||||||
brace_depth: BracketDepth, // {}
|
|
||||||
parenthesis_depth: BracketDepth, // ()
|
|
||||||
preceding_character: Option<char>,
|
preceding_character: Option<char>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'s> std::fmt::Debug for OrgSource<'s> {
|
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
||||||
f.debug_tuple("OrgSource")
|
|
||||||
.field(&Into::<&str>::into(self))
|
|
||||||
.finish()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'s> OrgSource<'s> {
|
impl<'s> OrgSource<'s> {
|
||||||
/// Returns a wrapped string that keeps track of values we need for parsing org-mode.
|
/// Returns a wrapped string that keeps track of values we need for parsing org-mode.
|
||||||
///
|
///
|
||||||
@@ -44,9 +31,6 @@ impl<'s> OrgSource<'s> {
|
|||||||
end: input.len(),
|
end: input.len(),
|
||||||
start_of_line: 0,
|
start_of_line: 0,
|
||||||
preceding_character: None,
|
preceding_character: None,
|
||||||
bracket_depth: 0,
|
|
||||||
brace_depth: 0,
|
|
||||||
parenthesis_depth: 0,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -72,18 +56,6 @@ impl<'s> OrgSource<'s> {
|
|||||||
assert!(other.end <= self.end);
|
assert!(other.end <= self.end);
|
||||||
self.slice(..(other.start - self.start))
|
self.slice(..(other.start - self.start))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_bracket_depth(&self) -> BracketDepth {
|
|
||||||
self.bracket_depth
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_brace_depth(&self) -> BracketDepth {
|
|
||||||
self.brace_depth
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_parenthesis_depth(&self) -> BracketDepth {
|
|
||||||
self.parenthesis_depth
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'s> InputTake for OrgSource<'s> {
|
impl<'s> InputTake for OrgSource<'s> {
|
||||||
@@ -147,36 +119,10 @@ where
|
|||||||
}
|
}
|
||||||
|
|
||||||
let skipped_text = &self.full_source[self.start..new_start];
|
let skipped_text = &self.full_source[self.start..new_start];
|
||||||
let mut start_of_line = self.start_of_line;
|
let start_of_line = skipped_text
|
||||||
let mut bracket_depth = self.bracket_depth;
|
.rfind('\n')
|
||||||
let mut brace_depth = self.brace_depth;
|
.map(|idx| self.start + idx + 1)
|
||||||
let mut parenthesis_depth = self.parenthesis_depth;
|
.unwrap_or(self.start_of_line);
|
||||||
for (offset, byte) in skipped_text.bytes().enumerate() {
|
|
||||||
match byte {
|
|
||||||
b'\n' => {
|
|
||||||
start_of_line = self.start + offset + 1;
|
|
||||||
}
|
|
||||||
b'[' => {
|
|
||||||
bracket_depth += 1;
|
|
||||||
}
|
|
||||||
b']' => {
|
|
||||||
bracket_depth -= 1;
|
|
||||||
}
|
|
||||||
b'{' => {
|
|
||||||
brace_depth += 1;
|
|
||||||
}
|
|
||||||
b'}' => {
|
|
||||||
brace_depth -= 1;
|
|
||||||
}
|
|
||||||
b'(' => {
|
|
||||||
parenthesis_depth += 1;
|
|
||||||
}
|
|
||||||
b')' => {
|
|
||||||
parenthesis_depth -= 1;
|
|
||||||
}
|
|
||||||
_ => {}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
OrgSource {
|
OrgSource {
|
||||||
full_source: self.full_source,
|
full_source: self.full_source,
|
||||||
@@ -184,9 +130,6 @@ where
|
|||||||
end: new_end,
|
end: new_end,
|
||||||
start_of_line,
|
start_of_line,
|
||||||
preceding_character: skipped_text.chars().last(),
|
preceding_character: skipped_text.chars().last(),
|
||||||
bracket_depth,
|
|
||||||
brace_depth,
|
|
||||||
parenthesis_depth,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -303,9 +246,7 @@ impl<'s> InputTakeAtPosition for OrgSource<'s> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn convert_error<'a, I: Into<CustomError<&'a str>>>(
|
pub fn convert_error(err: nom::Err<CustomError<OrgSource<'_>>>) -> nom::Err<CustomError<&str>> {
|
||||||
err: nom::Err<I>,
|
|
||||||
) -> nom::Err<CustomError<&'a str>> {
|
|
||||||
match err {
|
match err {
|
||||||
nom::Err::Incomplete(needed) => nom::Err::Incomplete(needed),
|
nom::Err::Incomplete(needed) => nom::Err::Incomplete(needed),
|
||||||
nom::Err::Error(err) => nom::Err::Error(err.into()),
|
nom::Err::Error(err) => nom::Err::Error(err.into()),
|
||||||
@@ -428,19 +369,4 @@ mod tests {
|
|||||||
assert_eq!(input.get_preceding_character(), None);
|
assert_eq!(input.get_preceding_character(), None);
|
||||||
assert_eq!(input.slice(8..).get_preceding_character(), Some('💛'));
|
assert_eq!(input.slice(8..).get_preceding_character(), Some('💛'));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn depth() {
|
|
||||||
let input = OrgSource::new("[][()][({)]}}}}");
|
|
||||||
assert_eq!(input.get_bracket_depth(), 0);
|
|
||||||
assert_eq!(input.get_brace_depth(), 0);
|
|
||||||
assert_eq!(input.get_parenthesis_depth(), 0);
|
|
||||||
assert_eq!(input.slice(4..).get_bracket_depth(), 1);
|
|
||||||
assert_eq!(input.slice(4..).get_brace_depth(), 0);
|
|
||||||
assert_eq!(input.slice(4..).get_parenthesis_depth(), 1);
|
|
||||||
assert_eq!(input.slice(4..).slice(6..).get_bracket_depth(), 1);
|
|
||||||
assert_eq!(input.slice(4..).slice(6..).get_brace_depth(), 1);
|
|
||||||
assert_eq!(input.slice(4..).slice(6..).get_parenthesis_depth(), 0);
|
|
||||||
assert_eq!(input.slice(14..).get_brace_depth(), -2);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -110,10 +110,11 @@ impl<'r, 's> ContextTree<'r, 's> {
|
|||||||
pub enum ContextElement<'r, 's> {
|
pub enum ContextElement<'r, 's> {
|
||||||
/// Stores a parser that indicates that children should exit upon matching an exit matcher.
|
/// Stores a parser that indicates that children should exit upon matching an exit matcher.
|
||||||
ExitMatcherNode(ExitMatcherNode<'r>),
|
ExitMatcherNode(ExitMatcherNode<'r>),
|
||||||
|
|
||||||
/// Stores the name of the current element to prevent directly nesting elements of the same type.
|
|
||||||
Context(&'r str),
|
Context(&'r str),
|
||||||
|
|
||||||
|
/// Stores the name of the greater block.
|
||||||
|
GreaterBlock(&'s str),
|
||||||
|
|
||||||
/// Indicates if elements should consume the whitespace after them.
|
/// Indicates if elements should consume the whitespace after them.
|
||||||
ConsumeTrailingWhitespace(bool),
|
ConsumeTrailingWhitespace(bool),
|
||||||
|
|
||||||
@@ -123,6 +124,71 @@ pub enum ContextElement<'r, 's> {
|
|||||||
/// org-mode document since text needs to be re-parsed to look for
|
/// org-mode document since text needs to be re-parsed to look for
|
||||||
/// radio links matching the contents of radio targets.
|
/// radio links matching the contents of radio targets.
|
||||||
RadioTarget(Vec<&'r Vec<Object<'s>>>),
|
RadioTarget(Vec<&'r Vec<Object<'s>>>),
|
||||||
|
|
||||||
|
/// Stores the current bracket depth inside a footnote reference's definition.
|
||||||
|
///
|
||||||
|
/// The definition inside a footnote reference must have balanced
|
||||||
|
/// brackets [] inside the definition, so this stores the amount
|
||||||
|
/// of opening brackets subtracted by the amount of closing
|
||||||
|
/// brackets within the definition must equal zero.
|
||||||
|
///
|
||||||
|
/// A reference to the position in the string is also included so
|
||||||
|
/// unbalanced brackets can be detected in the middle of an
|
||||||
|
/// object.
|
||||||
|
FootnoteReferenceDefinition(FootnoteReferenceDefinition<'s>),
|
||||||
|
|
||||||
|
/// Stores the current bracket depth inside a citation.
|
||||||
|
///
|
||||||
|
/// The global prefix, global suffix, key prefix, and key suffix
|
||||||
|
/// inside a footnote reference must have balanced brackets []
|
||||||
|
/// inside the definition, so this stores the amount of opening
|
||||||
|
/// brackets subtracted by the amount of closing brackets within
|
||||||
|
/// the definition must equal zero. None of the prefixes or
|
||||||
|
/// suffixes can be nested inside each other so we can use a
|
||||||
|
/// single type for this without conflict.
|
||||||
|
///
|
||||||
|
/// A reference to the position in the string is also included so
|
||||||
|
/// unbalanced brackets can be detected in the middle of an
|
||||||
|
/// object.
|
||||||
|
CitationBracket(CitationBracket<'s>),
|
||||||
|
|
||||||
|
/// Stores the current bracket or parenthesis depth inside an inline babel call.
|
||||||
|
///
|
||||||
|
/// Inside an inline babel call the headers must have balanced
|
||||||
|
/// parentheses () and the arguments must have balanced brackets
|
||||||
|
/// [], so this stores the amount of opening brackets subtracted
|
||||||
|
/// by the amount of closing brackets within the definition must
|
||||||
|
/// equal zero.
|
||||||
|
///
|
||||||
|
/// A reference to the position in the string is also included so
|
||||||
|
/// unbalanced brackets can be detected in the middle of an
|
||||||
|
/// object.
|
||||||
|
BabelHeaderBracket(BabelHeaderBracket<'s>),
|
||||||
|
|
||||||
|
/// Stores the current bracket or parenthesis depth inside an inline babel call.
|
||||||
|
///
|
||||||
|
/// Inside an inline babel call the headers must have balanced
|
||||||
|
/// parentheses () and the arguments must have balanced brackets
|
||||||
|
/// [], so this stores the amount of opening brackets subtracted
|
||||||
|
/// by the amount of closing brackets within the definition must
|
||||||
|
/// equal zero.
|
||||||
|
///
|
||||||
|
/// A reference to the position in the string is also included so
|
||||||
|
/// unbalanced brackets can be detected in the middle of an
|
||||||
|
/// object.
|
||||||
|
InlineSourceBlockBracket(InlineSourceBlockBracket<'s>),
|
||||||
|
|
||||||
|
/// Stores the current bracket or parenthesis depth inside a
|
||||||
|
/// superscript or superscript.
|
||||||
|
///
|
||||||
|
/// Inside the braces of a subscript or superscript there must be
|
||||||
|
/// balanced braces {}, so this stores the amount of opening
|
||||||
|
/// braces subtracted by the amount of closing braces within the
|
||||||
|
/// definition must equal zero.
|
||||||
|
///
|
||||||
|
/// A reference to the position in the string is also included so
|
||||||
|
/// unbalanced braces can be detected in the middle of an object.
|
||||||
|
SubscriptSuperscriptBrace(SubscriptSuperscriptBrace<'s>),
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct ExitMatcherNode<'r> {
|
pub struct ExitMatcherNode<'r> {
|
||||||
@@ -130,6 +196,36 @@ pub struct ExitMatcherNode<'r> {
|
|||||||
pub class: ExitClass,
|
pub class: ExitClass,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct FootnoteReferenceDefinition<'s> {
|
||||||
|
pub position: OrgSource<'s>,
|
||||||
|
pub depth: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct CitationBracket<'s> {
|
||||||
|
pub position: OrgSource<'s>,
|
||||||
|
pub depth: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct BabelHeaderBracket<'s> {
|
||||||
|
pub position: OrgSource<'s>,
|
||||||
|
pub depth: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct InlineSourceBlockBracket<'s> {
|
||||||
|
pub position: OrgSource<'s>,
|
||||||
|
pub depth: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct SubscriptSuperscriptBrace<'s> {
|
||||||
|
pub position: OrgSource<'s>,
|
||||||
|
pub depth: usize,
|
||||||
|
}
|
||||||
|
|
||||||
impl<'r> std::fmt::Debug for ExitMatcherNode<'r> {
|
impl<'r> std::fmt::Debug for ExitMatcherNode<'r> {
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
let mut formatter = f.debug_struct("ExitMatcherNode");
|
let mut formatter = f.debug_struct("ExitMatcherNode");
|
||||||
|
|||||||
@@ -7,7 +7,6 @@ use nom::character::complete::one_of;
|
|||||||
use nom::combinator::eof;
|
use nom::combinator::eof;
|
||||||
use nom::combinator::peek;
|
use nom::combinator::peek;
|
||||||
use nom::combinator::recognize;
|
use nom::combinator::recognize;
|
||||||
use nom::combinator::verify;
|
|
||||||
use nom::multi::many_till;
|
use nom::multi::many_till;
|
||||||
|
|
||||||
use super::org_source::OrgSource;
|
use super::org_source::OrgSource;
|
||||||
@@ -24,33 +23,6 @@ use crate::parser::util::exit_matcher_parser;
|
|||||||
use crate::parser::util::get_consumed;
|
use crate::parser::util::get_consumed;
|
||||||
use crate::parser::util::WORD_CONSTITUENT_CHARACTERS;
|
use crate::parser::util::WORD_CONSTITUENT_CHARACTERS;
|
||||||
|
|
||||||
// TODO: Make this a user-provided variable corresponding to elisp's org-link-parameters
|
|
||||||
const ORG_LINK_PARAMETERS: [&'static str; 23] = [
|
|
||||||
"id",
|
|
||||||
"eww",
|
|
||||||
"rmail",
|
|
||||||
"mhe",
|
|
||||||
"irc",
|
|
||||||
"info",
|
|
||||||
"gnus",
|
|
||||||
"docview",
|
|
||||||
"bibtex",
|
|
||||||
"bbdb",
|
|
||||||
"w3m",
|
|
||||||
"doi",
|
|
||||||
"file+sys",
|
|
||||||
"file+emacs",
|
|
||||||
"shell",
|
|
||||||
"news",
|
|
||||||
"mailto",
|
|
||||||
"https",
|
|
||||||
"http",
|
|
||||||
"ftp",
|
|
||||||
"help",
|
|
||||||
"file",
|
|
||||||
"elisp",
|
|
||||||
];
|
|
||||||
|
|
||||||
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
|
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
|
||||||
pub fn plain_link<'r, 's>(
|
pub fn plain_link<'r, 's>(
|
||||||
context: Context<'r, 's>,
|
context: Context<'r, 's>,
|
||||||
@@ -101,19 +73,36 @@ pub fn protocol<'r, 's>(
|
|||||||
input: OrgSource<'s>,
|
input: OrgSource<'s>,
|
||||||
) -> Res<OrgSource<'s>, OrgSource<'s>> {
|
) -> Res<OrgSource<'s>, OrgSource<'s>> {
|
||||||
// TODO: This should be defined by org-link-parameters
|
// TODO: This should be defined by org-link-parameters
|
||||||
for link_parameter in ORG_LINK_PARAMETERS {
|
let (remaining, proto) = alt((
|
||||||
let result = tag_no_case::<_, _, CustomError<_>>(link_parameter)(input);
|
alt((
|
||||||
match result {
|
tag_no_case("id"),
|
||||||
Ok((remaining, ent)) => {
|
tag_no_case("eww"),
|
||||||
return Ok((remaining, ent));
|
tag_no_case("rmail"),
|
||||||
}
|
tag_no_case("mhe"),
|
||||||
Err(_) => {}
|
tag_no_case("irc"),
|
||||||
}
|
tag_no_case("info"),
|
||||||
}
|
tag_no_case("gnus"),
|
||||||
|
tag_no_case("docview"),
|
||||||
Err(nom::Err::Error(CustomError::MyError(MyError(
|
tag_no_case("bibtex"),
|
||||||
"NoLinkProtocol".into(),
|
tag_no_case("bbdb"),
|
||||||
))))
|
tag_no_case("w3m"),
|
||||||
|
)),
|
||||||
|
alt((
|
||||||
|
tag_no_case("doi"),
|
||||||
|
tag_no_case("file+sys"),
|
||||||
|
tag_no_case("file+emacs"),
|
||||||
|
tag_no_case("shell"),
|
||||||
|
tag_no_case("news"),
|
||||||
|
tag_no_case("mailto"),
|
||||||
|
tag_no_case("https"),
|
||||||
|
tag_no_case("http"),
|
||||||
|
tag_no_case("ftp"),
|
||||||
|
tag_no_case("help"),
|
||||||
|
tag_no_case("file"),
|
||||||
|
tag_no_case("elisp"),
|
||||||
|
)),
|
||||||
|
))(input)?;
|
||||||
|
Ok((remaining, proto))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
|
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
|
||||||
@@ -130,11 +119,7 @@ fn path_plain<'r, 's>(
|
|||||||
|
|
||||||
let exit_matcher = parser_with_context!(exit_matcher_parser)(&parser_context);
|
let exit_matcher = parser_with_context!(exit_matcher_parser)(&parser_context);
|
||||||
|
|
||||||
let (remaining, path) = recognize(verify(
|
let (remaining, path) = recognize(many_till(anychar, peek(exit_matcher)))(input)?;
|
||||||
many_till(anychar, peek(exit_matcher)),
|
|
||||||
|(children, _exit_contents)| !children.is_empty(),
|
|
||||||
))(input)?;
|
|
||||||
|
|
||||||
Ok((remaining, path))
|
Ok((remaining, path))
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -143,10 +128,5 @@ fn path_plain_end<'r, 's>(
|
|||||||
_context: Context<'r, 's>,
|
_context: Context<'r, 's>,
|
||||||
input: OrgSource<'s>,
|
input: OrgSource<'s>,
|
||||||
) -> Res<OrgSource<'s>, OrgSource<'s>> {
|
) -> Res<OrgSource<'s>, OrgSource<'s>> {
|
||||||
recognize(many_till(
|
recognize(one_of(" \t\r\n()[]<>"))(input)
|
||||||
verify(anychar, |c| {
|
|
||||||
*c != '/' && (c.is_ascii_punctuation() || c.is_whitespace())
|
|
||||||
}),
|
|
||||||
one_of(" \t\r\n()[]<>"),
|
|
||||||
))(input)
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,24 +6,19 @@ use nom::character::complete::one_of;
|
|||||||
use nom::character::complete::space0;
|
use nom::character::complete::space0;
|
||||||
use nom::character::complete::space1;
|
use nom::character::complete::space1;
|
||||||
use nom::combinator::eof;
|
use nom::combinator::eof;
|
||||||
use nom::combinator::not;
|
|
||||||
use nom::combinator::opt;
|
use nom::combinator::opt;
|
||||||
use nom::combinator::peek;
|
|
||||||
use nom::combinator::recognize;
|
use nom::combinator::recognize;
|
||||||
use nom::combinator::verify;
|
use nom::combinator::verify;
|
||||||
use nom::multi::many0;
|
|
||||||
use nom::multi::many1;
|
use nom::multi::many1;
|
||||||
use nom::multi::many_till;
|
use nom::multi::many_till;
|
||||||
use nom::sequence::tuple;
|
use nom::sequence::tuple;
|
||||||
|
|
||||||
use super::greater_element::PlainList;
|
use super::greater_element::PlainList;
|
||||||
use super::greater_element::PlainListItem;
|
use super::greater_element::PlainListItem;
|
||||||
use super::object_parser::standard_set_object;
|
|
||||||
use super::org_source::OrgSource;
|
use super::org_source::OrgSource;
|
||||||
use super::parser_with_context::parser_with_context;
|
use super::parser_with_context::parser_with_context;
|
||||||
use super::util::non_whitespace_character;
|
use super::util::non_whitespace_character;
|
||||||
use super::Context;
|
use super::Context;
|
||||||
use super::Object;
|
|
||||||
use crate::error::CustomError;
|
use crate::error::CustomError;
|
||||||
use crate::error::MyError;
|
use crate::error::MyError;
|
||||||
use crate::error::Res;
|
use crate::error::Res;
|
||||||
@@ -42,13 +37,9 @@ pub fn detect_plain_list<'r, 's>(
|
|||||||
_context: Context<'r, 's>,
|
_context: Context<'r, 's>,
|
||||||
input: OrgSource<'s>,
|
input: OrgSource<'s>,
|
||||||
) -> Res<OrgSource<'s>, ()> {
|
) -> Res<OrgSource<'s>, ()> {
|
||||||
|
// TODO: Add support for plain list items that do not have content on the first line.
|
||||||
if verify(
|
if verify(
|
||||||
tuple((
|
tuple((start_of_line, space0, bullet, space1)),
|
||||||
start_of_line,
|
|
||||||
space0,
|
|
||||||
bullet,
|
|
||||||
alt((space1, line_ending, eof)),
|
|
||||||
)),
|
|
||||||
|(_start, indent, bull, _after_whitespace)| {
|
|(_start, indent, bull, _after_whitespace)| {
|
||||||
Into::<&str>::into(bull) != "*" || indent.len() > 0
|
Into::<&str>::into(bull) != "*" || indent.len() > 0
|
||||||
},
|
},
|
||||||
@@ -143,7 +134,9 @@ pub fn plain_list_item<'r, 's>(
|
|||||||
Into::<&str>::into(bull) != "*" || indent_level > 0
|
Into::<&str>::into(bull) != "*" || indent_level > 0
|
||||||
})(remaining)?;
|
})(remaining)?;
|
||||||
|
|
||||||
let maybe_contentless_item: Res<OrgSource<'_>, OrgSource<'_>> = eof(remaining);
|
// TODO: This isn't taking into account items that immediately line break and then have contents
|
||||||
|
let maybe_contentless_item: Res<OrgSource<'_>, OrgSource<'_>> =
|
||||||
|
alt((eof, line_ending))(remaining);
|
||||||
match maybe_contentless_item {
|
match maybe_contentless_item {
|
||||||
Ok((rem, _ws)) => {
|
Ok((rem, _ws)) => {
|
||||||
let source = get_consumed(input, rem);
|
let source = get_consumed(input, rem);
|
||||||
@@ -153,7 +146,6 @@ pub fn plain_list_item<'r, 's>(
|
|||||||
source: source.into(),
|
source: source.into(),
|
||||||
indentation: indent_level,
|
indentation: indent_level,
|
||||||
bullet: bull.into(),
|
bullet: bull.into(),
|
||||||
tag: Vec::new(),
|
|
||||||
children: Vec::new(),
|
children: Vec::new(),
|
||||||
},
|
},
|
||||||
));
|
));
|
||||||
@@ -161,12 +153,7 @@ pub fn plain_list_item<'r, 's>(
|
|||||||
Err(_) => {}
|
Err(_) => {}
|
||||||
};
|
};
|
||||||
|
|
||||||
let (remaining, maybe_tag) = opt(tuple((
|
let (remaining, _ws) = space1(remaining)?;
|
||||||
space1,
|
|
||||||
parser_with_context!(item_tag)(context),
|
|
||||||
tag(" ::"),
|
|
||||||
)))(remaining)?;
|
|
||||||
let (remaining, _ws) = item_tag_post_gap(context, remaining)?;
|
|
||||||
let exit_matcher = plain_list_item_end(indent_level);
|
let exit_matcher = plain_list_item_end(indent_level);
|
||||||
let parser_context = context
|
let parser_context = context
|
||||||
.with_additional_node(ContextElement::ConsumeTrailingWhitespace(true))
|
.with_additional_node(ContextElement::ConsumeTrailingWhitespace(true))
|
||||||
@@ -175,12 +162,12 @@ pub fn plain_list_item<'r, 's>(
|
|||||||
exit_matcher: &exit_matcher,
|
exit_matcher: &exit_matcher,
|
||||||
}));
|
}));
|
||||||
|
|
||||||
let (remaining, (children, _exit_contents)) = many_till(
|
let (remaining, (children, _exit_contents)) = verify(
|
||||||
parser_with_context!(element(true))(&parser_context),
|
many_till(
|
||||||
alt((
|
parser_with_context!(element(true))(&parser_context),
|
||||||
peek(recognize(tuple((start_of_line, many0(blank_line), eof)))),
|
|
||||||
parser_with_context!(exit_matcher_parser)(&parser_context),
|
parser_with_context!(exit_matcher_parser)(&parser_context),
|
||||||
)),
|
),
|
||||||
|
|(children, _exit_contents)| !children.is_empty(),
|
||||||
)(remaining)?;
|
)(remaining)?;
|
||||||
|
|
||||||
let (remaining, _trailing_ws) =
|
let (remaining, _trailing_ws) =
|
||||||
@@ -193,9 +180,6 @@ pub fn plain_list_item<'r, 's>(
|
|||||||
source: source.into(),
|
source: source.into(),
|
||||||
indentation: indent_level,
|
indentation: indent_level,
|
||||||
bullet: bull.into(),
|
bullet: bull.into(),
|
||||||
tag: maybe_tag
|
|
||||||
.map(|(_ws, item_tag, _divider)| item_tag)
|
|
||||||
.unwrap_or(Vec::new()),
|
|
||||||
children,
|
children,
|
||||||
},
|
},
|
||||||
));
|
));
|
||||||
@@ -278,60 +262,6 @@ fn _line_indented_lte<'r, 's>(
|
|||||||
Ok(matched)
|
Ok(matched)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
|
|
||||||
fn item_tag<'r, 's>(
|
|
||||||
context: Context<'r, 's>,
|
|
||||||
input: OrgSource<'s>,
|
|
||||||
) -> Res<OrgSource<'s>, Vec<Object<'s>>> {
|
|
||||||
let parser_context =
|
|
||||||
context.with_additional_node(ContextElement::ExitMatcherNode(ExitMatcherNode {
|
|
||||||
class: ExitClass::Gamma,
|
|
||||||
exit_matcher: &item_tag_end,
|
|
||||||
}));
|
|
||||||
let (remaining, (children, _exit_contents)) = verify(
|
|
||||||
many_till(
|
|
||||||
// TODO: Should this be using a different set like the minimal set?
|
|
||||||
parser_with_context!(standard_set_object)(&parser_context),
|
|
||||||
parser_with_context!(exit_matcher_parser)(&parser_context),
|
|
||||||
),
|
|
||||||
|(children, _exit_contents)| !children.is_empty(),
|
|
||||||
)(input)?;
|
|
||||||
Ok((remaining, children))
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
|
|
||||||
fn item_tag_end<'r, 's>(
|
|
||||||
_context: Context<'r, 's>,
|
|
||||||
input: OrgSource<'s>,
|
|
||||||
) -> Res<OrgSource<'s>, OrgSource<'s>> {
|
|
||||||
recognize(alt((
|
|
||||||
line_ending,
|
|
||||||
tag(" :: "),
|
|
||||||
recognize(tuple((tag(" ::"), alt((line_ending, eof))))),
|
|
||||||
)))(input)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
|
|
||||||
fn item_tag_post_gap<'r, 's>(
|
|
||||||
context: Context<'r, 's>,
|
|
||||||
input: OrgSource<'s>,
|
|
||||||
) -> Res<OrgSource<'s>, OrgSource<'s>> {
|
|
||||||
verify(
|
|
||||||
recognize(tuple((
|
|
||||||
alt((blank_line, space0)),
|
|
||||||
many_till(
|
|
||||||
blank_line,
|
|
||||||
alt((
|
|
||||||
peek(recognize(not(blank_line))),
|
|
||||||
peek(recognize(tuple((many0(blank_line), eof)))),
|
|
||||||
parser_with_context!(exit_matcher_parser)(context),
|
|
||||||
)),
|
|
||||||
),
|
|
||||||
))),
|
|
||||||
|gap| gap.len() > 0,
|
|
||||||
)(input)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
@@ -489,40 +419,4 @@ dolar"#,
|
|||||||
"#
|
"#
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn detect_line_break() {
|
|
||||||
let input = OrgSource::new(
|
|
||||||
r#"+
|
|
||||||
"#,
|
|
||||||
);
|
|
||||||
let initial_context: ContextTree<'_, '_> = ContextTree::new();
|
|
||||||
let result = detect_plain_list(&initial_context, input);
|
|
||||||
assert!(result.is_ok());
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn detect_eof() {
|
|
||||||
let input = OrgSource::new(r#"+"#);
|
|
||||||
let initial_context: ContextTree<'_, '_> = ContextTree::new();
|
|
||||||
let result = detect_plain_list(&initial_context, input);
|
|
||||||
assert!(result.is_ok());
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn detect_no_gap() {
|
|
||||||
let input = OrgSource::new(r#"+foo"#);
|
|
||||||
let initial_context: ContextTree<'_, '_> = ContextTree::new();
|
|
||||||
let result = detect_plain_list(&initial_context, input);
|
|
||||||
// Since there is no whitespace after the '+' this is a paragraph, not a plain list.
|
|
||||||
assert!(result.is_err());
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn detect_with_gap() {
|
|
||||||
let input = OrgSource::new(r#"+ foo"#);
|
|
||||||
let initial_context: ContextTree<'_, '_> = ContextTree::new();
|
|
||||||
let result = detect_plain_list(&initial_context, input);
|
|
||||||
assert!(result.is_ok());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,14 +2,13 @@ use nom::branch::alt;
|
|||||||
use nom::bytes::complete::is_not;
|
use nom::bytes::complete::is_not;
|
||||||
use nom::bytes::complete::tag;
|
use nom::bytes::complete::tag;
|
||||||
use nom::bytes::complete::tag_no_case;
|
use nom::bytes::complete::tag_no_case;
|
||||||
use nom::character::complete::anychar;
|
|
||||||
use nom::character::complete::line_ending;
|
use nom::character::complete::line_ending;
|
||||||
use nom::character::complete::space0;
|
use nom::character::complete::space0;
|
||||||
use nom::character::complete::space1;
|
use nom::character::complete::space1;
|
||||||
use nom::combinator::eof;
|
use nom::combinator::eof;
|
||||||
|
use nom::combinator::map;
|
||||||
use nom::combinator::opt;
|
use nom::combinator::opt;
|
||||||
use nom::combinator::recognize;
|
use nom::combinator::recognize;
|
||||||
use nom::combinator::verify;
|
|
||||||
use nom::multi::many_till;
|
use nom::multi::many_till;
|
||||||
use nom::sequence::tuple;
|
use nom::sequence::tuple;
|
||||||
|
|
||||||
@@ -24,6 +23,7 @@ use crate::parser::greater_element::PropertyDrawer;
|
|||||||
use crate::parser::parser_context::ContextElement;
|
use crate::parser::parser_context::ContextElement;
|
||||||
use crate::parser::parser_context::ExitMatcherNode;
|
use crate::parser::parser_context::ExitMatcherNode;
|
||||||
use crate::parser::parser_with_context::parser_with_context;
|
use crate::parser::parser_with_context::parser_with_context;
|
||||||
|
use crate::parser::plain_text::plain_text;
|
||||||
use crate::parser::util::exit_matcher_parser;
|
use crate::parser::util::exit_matcher_parser;
|
||||||
use crate::parser::util::get_consumed;
|
use crate::parser::util::get_consumed;
|
||||||
use crate::parser::util::immediate_in_section;
|
use crate::parser::util::immediate_in_section;
|
||||||
@@ -147,16 +147,11 @@ fn node_property_name<'r, 's>(
|
|||||||
}));
|
}));
|
||||||
|
|
||||||
let (remaining, name) = recognize(tuple((
|
let (remaining, name) = recognize(tuple((
|
||||||
verify(
|
map(parser_with_context!(plain_text)(&parser_context), |pt| {
|
||||||
many_till(
|
pt.source
|
||||||
anychar,
|
}),
|
||||||
parser_with_context!(exit_matcher_parser)(&parser_context),
|
|
||||||
),
|
|
||||||
|(children, _exit_contents)| !children.is_empty(),
|
|
||||||
),
|
|
||||||
opt(tag("+")),
|
opt(tag("+")),
|
||||||
)))(input)?;
|
)))(input)?;
|
||||||
|
|
||||||
Ok((remaining, name))
|
Ok((remaining, name))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -35,6 +35,44 @@ pub struct TextWithProperties<'s> {
|
|||||||
pub properties: Vec<Token<'s>>,
|
pub properties: Vec<Token<'s>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<'s> TextWithProperties<'s> {
|
||||||
|
pub fn unquote(&self) -> Result<String, Box<dyn std::error::Error>> {
|
||||||
|
let mut out = String::with_capacity(self.text.len());
|
||||||
|
if !self.text.starts_with(r#"""#) {
|
||||||
|
return Err("Quoted text does not start with quote.".into());
|
||||||
|
}
|
||||||
|
if !self.text.ends_with(r#"""#) {
|
||||||
|
return Err("Quoted text does not end with quote.".into());
|
||||||
|
}
|
||||||
|
let interior_text = &self.text[1..(self.text.len() - 1)];
|
||||||
|
let mut state = ParseState::Normal;
|
||||||
|
for current_char in interior_text.chars().into_iter() {
|
||||||
|
state = match (state, current_char) {
|
||||||
|
(ParseState::Normal, '\\') => ParseState::Escape,
|
||||||
|
(ParseState::Normal, _) => {
|
||||||
|
out.push(current_char);
|
||||||
|
ParseState::Normal
|
||||||
|
}
|
||||||
|
(ParseState::Escape, 'n') => {
|
||||||
|
out.push('\n');
|
||||||
|
ParseState::Normal
|
||||||
|
}
|
||||||
|
(ParseState::Escape, '\\') => {
|
||||||
|
out.push('\\');
|
||||||
|
ParseState::Normal
|
||||||
|
}
|
||||||
|
(ParseState::Escape, '"') => {
|
||||||
|
out.push('"');
|
||||||
|
ParseState::Normal
|
||||||
|
}
|
||||||
|
_ => todo!(),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(out)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
enum ParseState {
|
enum ParseState {
|
||||||
Normal,
|
Normal,
|
||||||
Escape,
|
Escape,
|
||||||
@@ -44,28 +82,28 @@ impl<'s> Token<'s> {
|
|||||||
pub fn as_vector<'p>(&'p self) -> Result<&'p Vec<Token<'s>>, Box<dyn std::error::Error>> {
|
pub fn as_vector<'p>(&'p self) -> Result<&'p Vec<Token<'s>>, Box<dyn std::error::Error>> {
|
||||||
Ok(match self {
|
Ok(match self {
|
||||||
Token::Vector(children) => Ok(children),
|
Token::Vector(children) => Ok(children),
|
||||||
_ => Err(format!("wrong token type, expected vector: {:?}", self)),
|
_ => Err(format!("wrong token type {:?}", self)),
|
||||||
}?)
|
}?)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn as_list<'p>(&'p self) -> Result<&'p Vec<Token<'s>>, Box<dyn std::error::Error>> {
|
pub fn as_list<'p>(&'p self) -> Result<&'p Vec<Token<'s>>, Box<dyn std::error::Error>> {
|
||||||
Ok(match self {
|
Ok(match self {
|
||||||
Token::List(children) => Ok(children),
|
Token::List(children) => Ok(children),
|
||||||
_ => Err(format!("wrong token type, expected list: {:?}", self)),
|
_ => Err(format!("wrong token type {:?}", self)),
|
||||||
}?)
|
}?)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn as_atom<'p>(&'p self) -> Result<&'s str, Box<dyn std::error::Error>> {
|
pub fn as_atom<'p>(&'p self) -> Result<&'s str, Box<dyn std::error::Error>> {
|
||||||
Ok(match self {
|
Ok(match self {
|
||||||
Token::Atom(body) => Ok(*body),
|
Token::Atom(body) => Ok(*body),
|
||||||
_ => Err(format!("wrong token type, expected atom: {:?}", self)),
|
_ => Err(format!("wrong token type {:?}", self)),
|
||||||
}?)
|
}?)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn as_text<'p>(&'p self) -> Result<&'p TextWithProperties<'s>, Box<dyn std::error::Error>> {
|
pub fn as_text<'p>(&'p self) -> Result<&'p TextWithProperties<'s>, Box<dyn std::error::Error>> {
|
||||||
Ok(match self {
|
Ok(match self {
|
||||||
Token::TextWithProperties(body) => Ok(body),
|
Token::TextWithProperties(body) => Ok(body),
|
||||||
_ => Err(format!("wrong token type, expected text: {:?}", self)),
|
_ => Err(format!("wrong token type {:?}", self)),
|
||||||
}?)
|
}?)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -95,42 +133,6 @@ impl<'s> Token<'s> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn unquote(text: &str) -> Result<String, Box<dyn std::error::Error>> {
|
|
||||||
let mut out = String::with_capacity(text.len());
|
|
||||||
if !text.starts_with(r#"""#) {
|
|
||||||
return Err("Quoted text does not start with quote.".into());
|
|
||||||
}
|
|
||||||
if !text.ends_with(r#"""#) {
|
|
||||||
return Err("Quoted text does not end with quote.".into());
|
|
||||||
}
|
|
||||||
let interior_text = &text[1..(text.len() - 1)];
|
|
||||||
let mut state = ParseState::Normal;
|
|
||||||
for current_char in interior_text.chars().into_iter() {
|
|
||||||
state = match (state, current_char) {
|
|
||||||
(ParseState::Normal, '\\') => ParseState::Escape,
|
|
||||||
(ParseState::Normal, _) => {
|
|
||||||
out.push(current_char);
|
|
||||||
ParseState::Normal
|
|
||||||
}
|
|
||||||
(ParseState::Escape, 'n') => {
|
|
||||||
out.push('\n');
|
|
||||||
ParseState::Normal
|
|
||||||
}
|
|
||||||
(ParseState::Escape, '\\') => {
|
|
||||||
out.push('\\');
|
|
||||||
ParseState::Normal
|
|
||||||
}
|
|
||||||
(ParseState::Escape, '"') => {
|
|
||||||
out.push('"');
|
|
||||||
ParseState::Normal
|
|
||||||
}
|
|
||||||
_ => todo!(),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(out)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
|
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
|
||||||
pub fn sexp_with_padding<'s>(input: &'s str) -> Res<&'s str, Token<'s>> {
|
pub fn sexp_with_padding<'s>(input: &'s str) -> Res<&'s str, Token<'s>> {
|
||||||
let (remaining, _) = multispace0(input)?;
|
let (remaining, _) = multispace0(input)?;
|
||||||
|
|||||||
@@ -11,7 +11,6 @@ use nom::combinator::recognize;
|
|||||||
use nom::combinator::verify;
|
use nom::combinator::verify;
|
||||||
use nom::multi::many_till;
|
use nom::multi::many_till;
|
||||||
|
|
||||||
use super::org_source::BracketDepth;
|
|
||||||
use super::org_source::OrgSource;
|
use super::org_source::OrgSource;
|
||||||
use super::Context;
|
use super::Context;
|
||||||
use super::Object;
|
use super::Object;
|
||||||
@@ -22,6 +21,7 @@ use crate::parser::exiting::ExitClass;
|
|||||||
use crate::parser::object_parser::standard_set_object;
|
use crate::parser::object_parser::standard_set_object;
|
||||||
use crate::parser::parser_context::ContextElement;
|
use crate::parser::parser_context::ContextElement;
|
||||||
use crate::parser::parser_context::ExitMatcherNode;
|
use crate::parser::parser_context::ExitMatcherNode;
|
||||||
|
use crate::parser::parser_context::SubscriptSuperscriptBrace;
|
||||||
use crate::parser::parser_with_context::parser_with_context;
|
use crate::parser::parser_with_context::parser_with_context;
|
||||||
use crate::parser::util::exit_matcher_parser;
|
use crate::parser::util::exit_matcher_parser;
|
||||||
use crate::parser::util::get_consumed;
|
use crate::parser::util::get_consumed;
|
||||||
@@ -154,11 +154,16 @@ fn script_with_braces<'r, 's>(
|
|||||||
input: OrgSource<'s>,
|
input: OrgSource<'s>,
|
||||||
) -> Res<OrgSource<'s>, Vec<Object<'s>>> {
|
) -> Res<OrgSource<'s>, Vec<Object<'s>>> {
|
||||||
let (remaining, _) = tag("{")(input)?;
|
let (remaining, _) = tag("{")(input)?;
|
||||||
let exit_with_depth = script_with_braces_end(remaining.get_brace_depth());
|
let parser_context = context
|
||||||
let parser_context =
|
.with_additional_node(ContextElement::SubscriptSuperscriptBrace(
|
||||||
context.with_additional_node(ContextElement::ExitMatcherNode(ExitMatcherNode {
|
SubscriptSuperscriptBrace {
|
||||||
|
position: remaining.into(),
|
||||||
|
depth: 0,
|
||||||
|
},
|
||||||
|
))
|
||||||
|
.with_additional_node(ContextElement::ExitMatcherNode(ExitMatcherNode {
|
||||||
class: ExitClass::Gamma,
|
class: ExitClass::Gamma,
|
||||||
exit_matcher: &exit_with_depth,
|
exit_matcher: &script_with_braces_end,
|
||||||
}));
|
}));
|
||||||
|
|
||||||
let (remaining, (children, _exit_contents)) = many_till(
|
let (remaining, (children, _exit_contents)) = many_till(
|
||||||
@@ -170,30 +175,49 @@ fn script_with_braces<'r, 's>(
|
|||||||
Ok((remaining, children))
|
Ok((remaining, children))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn script_with_braces_end(
|
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
|
||||||
starting_brace_depth: BracketDepth,
|
fn script_with_braces_end<'r, 's>(
|
||||||
) -> impl for<'r, 's> Fn(Context<'r, 's>, OrgSource<'s>) -> Res<OrgSource<'s>, OrgSource<'s>> {
|
context: Context<'r, 's>,
|
||||||
move |context: Context, input: OrgSource<'_>| {
|
input: OrgSource<'s>,
|
||||||
_script_with_braces_end(context, input, starting_brace_depth)
|
) -> Res<OrgSource<'s>, OrgSource<'s>> {
|
||||||
|
let context_depth = get_bracket_depth(context)
|
||||||
|
.expect("This function should only be called from inside a subscript or superscript.");
|
||||||
|
let text_since_context_entry = get_consumed(context_depth.position, input);
|
||||||
|
let mut current_depth = context_depth.depth;
|
||||||
|
for c in Into::<&str>::into(text_since_context_entry).chars() {
|
||||||
|
match c {
|
||||||
|
'{' => {
|
||||||
|
current_depth += 1;
|
||||||
|
}
|
||||||
|
'}' if current_depth == 0 => {
|
||||||
|
panic!("Exceeded subscript or superscript brace depth.")
|
||||||
|
}
|
||||||
|
'}' if current_depth > 0 => {
|
||||||
|
current_depth -= 1;
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
if current_depth == 0 {
|
||||||
|
let close_bracket = tag::<&str, OrgSource<'_>, CustomError<OrgSource<'_>>>("}")(input);
|
||||||
|
if close_bracket.is_ok() {
|
||||||
|
return close_bracket;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return Err(nom::Err::Error(CustomError::MyError(MyError(
|
||||||
|
"Not a valid end for subscript or superscript.".into(),
|
||||||
|
))));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
|
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
|
||||||
fn _script_with_braces_end<'r, 's>(
|
fn get_bracket_depth<'r, 's>(
|
||||||
_context: Context<'r, 's>,
|
context: Context<'r, 's>,
|
||||||
input: OrgSource<'s>,
|
) -> Option<&'r SubscriptSuperscriptBrace<'s>> {
|
||||||
starting_brace_depth: BracketDepth,
|
for node in context.iter() {
|
||||||
) -> Res<OrgSource<'s>, OrgSource<'s>> {
|
match node.get_data() {
|
||||||
let current_depth = input.get_brace_depth() - starting_brace_depth;
|
ContextElement::SubscriptSuperscriptBrace(depth) => return Some(depth),
|
||||||
if current_depth > 0 {
|
_ => {}
|
||||||
// Its impossible for the next character to end the subscript or superscript if we're any amount of braces deep
|
}
|
||||||
return Err(nom::Err::Error(CustomError::MyError(MyError(
|
|
||||||
"Not a valid end for subscript or superscript.".into(),
|
|
||||||
))));
|
|
||||||
}
|
}
|
||||||
if current_depth < 0 {
|
None
|
||||||
// This shouldn't be possible because if depth is 0 then a closing brace should end the subscript or superscript.
|
|
||||||
unreachable!("Exceeded subscript or superscript brace depth.")
|
|
||||||
}
|
|
||||||
tag("}")(input)
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,13 +12,14 @@ use nom::multi::many1;
|
|||||||
use nom::multi::many_till;
|
use nom::multi::many_till;
|
||||||
use nom::sequence::tuple;
|
use nom::sequence::tuple;
|
||||||
|
|
||||||
use super::object_parser::table_cell_set_object;
|
|
||||||
use super::org_source::OrgSource;
|
use super::org_source::OrgSource;
|
||||||
use super::Context;
|
use super::Context;
|
||||||
use crate::error::Res;
|
use crate::error::Res;
|
||||||
use crate::parser::exiting::ExitClass;
|
use crate::parser::exiting::ExitClass;
|
||||||
use crate::parser::greater_element::TableRow;
|
use crate::parser::greater_element::TableRow;
|
||||||
use crate::parser::lesser_element::TableCell;
|
use crate::parser::lesser_element::TableCell;
|
||||||
|
use crate::parser::object::Object;
|
||||||
|
use crate::parser::object_parser::minimal_set_object;
|
||||||
use crate::parser::parser_context::ContextElement;
|
use crate::parser::parser_context::ContextElement;
|
||||||
use crate::parser::parser_context::ExitMatcherNode;
|
use crate::parser::parser_context::ExitMatcherNode;
|
||||||
use crate::parser::parser_with_context::parser_with_context;
|
use crate::parser::parser_with_context::parser_with_context;
|
||||||
@@ -161,3 +162,14 @@ fn org_mode_table_cell_end<'r, 's>(
|
|||||||
) -> Res<OrgSource<'s>, OrgSource<'s>> {
|
) -> Res<OrgSource<'s>, OrgSource<'s>> {
|
||||||
recognize(tuple((space0, alt((tag("|"), peek(line_ending))))))(input)
|
recognize(tuple((space0, alt((tag("|"), peek(line_ending))))))(input)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
|
||||||
|
pub fn table_cell_set_object<'r, 's>(
|
||||||
|
context: Context<'r, 's>,
|
||||||
|
input: OrgSource<'s>,
|
||||||
|
) -> Res<OrgSource<'s>, Object<'s>> {
|
||||||
|
not(|i| context.check_exit_matcher(i))(input)?;
|
||||||
|
|
||||||
|
parser_with_context!(minimal_set_object)(context)(input)
|
||||||
|
// TODO: add citations, export snippets, footnote references, links, macros, radio targets, targets, and timestamps.
|
||||||
|
}
|
||||||
|
|||||||
@@ -155,16 +155,3 @@ pub fn not_yet_implemented() -> Res<OrgSource<'static>, ()> {
|
|||||||
"Not implemented yet.".into(),
|
"Not implemented yet.".into(),
|
||||||
))));
|
))));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(dead_code)]
|
|
||||||
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
|
|
||||||
/// Text from the current point until the next line break or end of file
|
|
||||||
///
|
|
||||||
/// Useful for debugging.
|
|
||||||
pub fn text_until_eol<'r, 's>(
|
|
||||||
input: OrgSource<'s>,
|
|
||||||
) -> Result<&'s str, nom::Err<CustomError<OrgSource<'s>>>> {
|
|
||||||
let line = recognize(many_till(anychar, alt((line_ending, eof))))(input)
|
|
||||||
.map(|(_remaining, line)| Into::<&str>::into(line))?;
|
|
||||||
Ok(line.trim())
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ fn {name}() {{
|
|||||||
let diff_result =
|
let diff_result =
|
||||||
compare_document(&parsed_sexp, &rust_parsed).expect("Compare parsed documents.");
|
compare_document(&parsed_sexp, &rust_parsed).expect("Compare parsed documents.");
|
||||||
diff_result
|
diff_result
|
||||||
.print(org_contents.as_str())
|
.print()
|
||||||
.expect("Print document parse tree diff.");
|
.expect("Print document parse tree diff.");
|
||||||
assert!(!diff_result.is_bad());
|
assert!(!diff_result.is_bad());
|
||||||
assert_eq!(remaining, "");
|
assert_eq!(remaining, "");
|
||||||
|
|||||||
Reference in New Issue
Block a user