Compare commits
34 Commits
8450785186
...
v0.1.8
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
de87b7df93 | ||
|
|
a267d13fd7 | ||
|
|
a29973a110 | ||
|
|
31c782499e | ||
|
|
b7c7057095 | ||
|
|
49e3c90a3a | ||
|
|
129228c5c5 | ||
|
|
f0a7493a89 | ||
|
|
dc5695ec9f | ||
|
|
4ff62fbfae | ||
|
|
c892d406c3 | ||
|
|
1a41cfc6c7 | ||
|
|
4f34ab9089 | ||
|
|
9b2348c0ef | ||
|
|
5716cbccea | ||
|
|
124cd50243 | ||
|
|
bac5d6e1d9 | ||
|
|
ba15999534 | ||
|
|
61c3e6c10e | ||
|
|
a7e130838d | ||
|
|
853adadf91 | ||
|
|
7b61329889 | ||
|
|
9bcfb2f1da | ||
|
|
4c8d9a3063 | ||
|
|
48cb3c4a02 | ||
|
|
9e60ff6683 | ||
|
|
c1de001786 | ||
|
|
716af5bb45 | ||
|
|
6137a46231 | ||
|
|
bdd04f4d5c | ||
|
|
36bdc54703 | ||
|
|
3031b6edd4 | ||
|
|
1a704dd312 | ||
|
|
a74ea730f4 |
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "organic"
|
name = "organic"
|
||||||
version = "0.1.7"
|
version = "0.1.8"
|
||||||
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"
|
||||||
|
|||||||
4
Makefile
4
Makefile
@@ -33,6 +33,10 @@ release:
|
|||||||
clean:
|
clean:
|
||||||
> cargo clean
|
> cargo clean
|
||||||
|
|
||||||
|
.PHONY: format
|
||||||
|
format:
|
||||||
|
> $(MAKE) -C docker/cargo_fmt run
|
||||||
|
|
||||||
.PHONY: test
|
.PHONY: test
|
||||||
test:
|
test:
|
||||||
> cargo test --no-default-features --features compare --no-fail-fast --lib --test test_loader -- --test-threads $(TESTJOBS)
|
> cargo test --no-default-features --features compare --no-fail-fast --lib --test test_loader -- --test-threads $(TESTJOBS)
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
FROM alpine:3.17 AS build
|
FROM alpine:3.17 AS build
|
||||||
RUN apk add --no-cache build-base musl-dev git autoconf make texinfo gnutls-dev ncurses-dev gawk
|
RUN apk add --no-cache build-base musl-dev git autoconf make texinfo gnutls-dev ncurses-dev gawk libgccjit-dev
|
||||||
|
|
||||||
|
|
||||||
FROM build AS build-emacs
|
FROM build AS build-emacs
|
||||||
@@ -8,13 +8,13 @@ RUN git clone --depth 1 --branch $EMACS_VERSION https://git.savannah.gnu.org/git
|
|||||||
WORKDIR /root/emacs
|
WORKDIR /root/emacs
|
||||||
RUN mkdir /root/dist
|
RUN mkdir /root/dist
|
||||||
RUN ./autogen.sh
|
RUN ./autogen.sh
|
||||||
RUN ./configure --prefix /usr --without-x --without-sound
|
RUN ./configure --prefix /usr --without-x --without-sound --with-native-compilation=aot
|
||||||
RUN make
|
RUN make
|
||||||
RUN make DESTDIR="/root/dist" install
|
RUN make DESTDIR="/root/dist" install
|
||||||
|
|
||||||
|
|
||||||
FROM build AS build-org-mode
|
FROM build AS build-org-mode
|
||||||
ARG ORG_VERSION=163bafb43dcc2bc94a2c7ccaa77d3d1dd488f1af
|
ARG ORG_VERSION=c703541ffcc14965e3567f928de1683a1c1e33f6
|
||||||
COPY --from=build-emacs /root/dist/ /
|
COPY --from=build-emacs /root/dist/ /
|
||||||
RUN mkdir /root/dist
|
RUN mkdir /root/dist
|
||||||
# Savannah does not allow fetching specific revisions, so we're going to have to put unnecessary load on their server by cloning main and then checking out the revision we want.
|
# Savannah does not allow fetching specific revisions, so we're going to have to put unnecessary load on their server by cloning main and then checking out the revision we want.
|
||||||
@@ -27,7 +27,7 @@ RUN make DESTDIR="/root/dist" install
|
|||||||
|
|
||||||
FROM rustlang/rust:nightly-alpine3.17 AS tester
|
FROM rustlang/rust:nightly-alpine3.17 AS tester
|
||||||
ENV LANG=en_US.UTF-8
|
ENV LANG=en_US.UTF-8
|
||||||
RUN apk add --no-cache musl-dev ncurses gnutls
|
RUN apk add --no-cache musl-dev ncurses gnutls libgccjit
|
||||||
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/ /
|
||||||
@@ -88,7 +88,7 @@ ARG DOOMEMACS_PATH=/foreign_documents/doomemacs
|
|||||||
ARG DOOMEMACS_REPO=https://github.com/doomemacs/doomemacs.git
|
ARG DOOMEMACS_REPO=https://github.com/doomemacs/doomemacs.git
|
||||||
RUN mkdir -p $DOOMEMACS_PATH && git -C $DOOMEMACS_PATH init --initial-branch=main && git -C $DOOMEMACS_PATH remote add origin $DOOMEMACS_REPO && git -C $DOOMEMACS_PATH fetch origin $DOOMEMACS_VERSION && git -C $DOOMEMACS_PATH checkout FETCH_HEAD
|
RUN mkdir -p $DOOMEMACS_PATH && git -C $DOOMEMACS_PATH init --initial-branch=main && git -C $DOOMEMACS_PATH remote add origin $DOOMEMACS_REPO && git -C $DOOMEMACS_PATH fetch origin $DOOMEMACS_VERSION && git -C $DOOMEMACS_PATH checkout FETCH_HEAD
|
||||||
|
|
||||||
ARG WORG_VERSION=74e80b0f7600801b1d1594542602394c085cc2f9
|
ARG WORG_VERSION=0c8d5679b536af450b61812246a3e02b8103f4b8
|
||||||
ARG WORG_PATH=/foreign_documents/worg
|
ARG WORG_PATH=/foreign_documents/worg
|
||||||
ARG WORG_REPO=https://git.sr.ht/~bzg/worg
|
ARG WORG_REPO=https://git.sr.ht/~bzg/worg
|
||||||
RUN mkdir -p $WORG_PATH && git -C $WORG_PATH init --initial-branch=main && git -C $WORG_PATH remote add origin $WORG_REPO && git -C $WORG_PATH fetch origin $WORG_VERSION && git -C $WORG_PATH checkout FETCH_HEAD
|
RUN mkdir -p $WORG_PATH && git -C $WORG_PATH init --initial-branch=main && git -C $WORG_PATH remote add origin $WORG_REPO && git -C $WORG_PATH fetch origin $WORG_VERSION && git -C $WORG_PATH checkout FETCH_HEAD
|
||||||
|
|||||||
@@ -41,9 +41,9 @@ function main {
|
|||||||
|
|
||||||
set -e
|
set -e
|
||||||
if [ "$all_status" -ne 0 ]; then
|
if [ "$all_status" -ne 0 ]; then
|
||||||
echo "$(red_text "Some tests failed.")"
|
red_text "Some tests failed."
|
||||||
else
|
else
|
||||||
echo "$(green_text "All tests passed.")"
|
green_text "All tests passed."
|
||||||
fi
|
fi
|
||||||
return "$all_status"
|
return "$all_status"
|
||||||
}
|
}
|
||||||
@@ -64,8 +64,9 @@ function indent {
|
|||||||
local depth="$1"
|
local depth="$1"
|
||||||
local scaled_depth=$((depth * 2))
|
local scaled_depth=$((depth * 2))
|
||||||
shift 1
|
shift 1
|
||||||
local prefix=$(printf -- "%${scaled_depth}s")
|
local prefix
|
||||||
while read l; do
|
prefix=$(printf -- "%${scaled_depth}s")
|
||||||
|
while read -r l; do
|
||||||
(IFS=' '; printf -- '%s%s\n' "$prefix" "$l")
|
(IFS=' '; printf -- '%s%s\n' "$prefix" "$l")
|
||||||
done
|
done
|
||||||
}
|
}
|
||||||
@@ -93,12 +94,13 @@ function compare_all_org_document {
|
|||||||
local target_document
|
local target_document
|
||||||
local all_status=0
|
local all_status=0
|
||||||
while read target_document; do
|
while read target_document; do
|
||||||
local relative_path=$($REALPATH --relative-to "$root_dir" "$target_document")
|
local relative_path
|
||||||
|
relative_path=$($REALPATH --relative-to "$root_dir" "$target_document")
|
||||||
set +e
|
set +e
|
||||||
(run_compare "$relative_path" "$target_document")
|
(run_compare "$relative_path" "$target_document")
|
||||||
if [ "$?" -ne 0 ]; then all_status=1; fi
|
if [ "$?" -ne 0 ]; then all_status=1; fi
|
||||||
set -e
|
set -e
|
||||||
done<<<$(find "$root_dir" -type f -iname '*.org')
|
done<<<"$(find "$root_dir" -type f -iname '*.org' | sort)"
|
||||||
return "$all_status"
|
return "$all_status"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,3 @@
|
|||||||
|
#+BEGIN: timestamp :format "%Y-%m-%d %H:%M"
|
||||||
|
|
||||||
|
#+END
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
- foo ::
|
||||||
|
|
||||||
|
- bar ::
|
||||||
|
|
||||||
|
|
||||||
|
baz
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
1. foo
|
||||||
|
- bar
|
||||||
|
- lorem :: ipsum
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
# Since this is an ordered list, the text before the " :: " is NOT parsed as a tag.
|
||||||
|
1. foo :: bar
|
||||||
4
org_mode_samples/greater_element/table/empty_formula.org
Normal file
4
org_mode_samples/greater_element/table/empty_formula.org
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
| Name | Value |
|
||||||
|
|------+-------|
|
||||||
|
| foo | bar |
|
||||||
|
#+tblfm:
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
# Fixed width areas must begin with colon followed by a space, not a tab, so this is not a fixed width area.
|
||||||
|
: foo
|
||||||
11
org_mode_samples/object/plain_link/with_parenthesis.org
Normal file
11
org_mode_samples/object/plain_link/with_parenthesis.org
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
# Should be a link:
|
||||||
|
https://en.wikipedia.org/wiki/Shebang_(Unix)
|
||||||
|
|
||||||
|
# No closing parenthesis, so link ends at underscore.
|
||||||
|
https://en.wikipedia.org/wiki/Shebang_(Unix
|
||||||
|
|
||||||
|
# Parenthesis only allowed to depth of 2 so link ends at underscore.
|
||||||
|
https://en.wikipedia.org/wiki/Shebang_(((Unix)))
|
||||||
|
|
||||||
|
# Even though they eventually become balanced, we hit negative parenthesis depth so link ends at )
|
||||||
|
https://en.wikipedia.org/wiki/Shebang)Unix(
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
[[https://en.wikipedia.org/wiki/Shebang_(Unix)]]
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
[[[http://foo.bar/baz][lorem]]]
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
# Even though *exporting* honors the setting to require braces for subscript/superscript, the official org-mode parser still parses subscripts and superscripts.
|
||||||
|
|
||||||
|
#+OPTIONS: ^:{}
|
||||||
|
foo_this isn't a subscript when exported due to lack of braces (but its still a subscript during parsing)
|
||||||
|
|
||||||
|
|
||||||
|
bar_{this is a subscript}
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
foo_(bar)
|
||||||
|
|
||||||
|
foo_(b(ar)
|
||||||
|
|
||||||
|
foo_(b{ar)
|
||||||
|
|
||||||
|
foo_{b(ar}
|
||||||
|
|
||||||
|
foo_(b(a)r)
|
||||||
|
|
||||||
|
foo_b(a)r
|
||||||
|
|
||||||
|
foo_(b+ar)
|
||||||
1
org_mode_samples/object/text_markup/double_star.org
Normal file
1
org_mode_samples/object/text_markup/double_star.org
Normal file
@@ -0,0 +1 @@
|
|||||||
|
foo ** bar ** baz
|
||||||
1
org_mode_samples/object/text_markup/double_tilde.org
Normal file
1
org_mode_samples/object/text_markup/double_tilde.org
Normal file
@@ -0,0 +1 @@
|
|||||||
|
foo ~~ bar ~~ baz
|
||||||
4
org_mode_samples/object/text_markup/target_substring.org
Normal file
4
org_mode_samples/object/text_markup/target_substring.org
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
# Since "foos" has an extra "s", this does not match the target.
|
||||||
|
the foos bar
|
||||||
|
|
||||||
|
The <<<foo>>> and stuff.
|
||||||
@@ -4,3 +4,5 @@
|
|||||||
* Baz
|
* Baz
|
||||||
*** Lorem
|
*** Lorem
|
||||||
* Ipsum
|
* Ipsum
|
||||||
|
**** Dolar
|
||||||
|
***** Cat
|
||||||
|
|||||||
@@ -8,10 +8,26 @@ DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
|
|||||||
: ${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
|
: ${NO_COLOR:=""} # Set to anything to disable color output
|
||||||
|
: ${PROFILE:="debug"}
|
||||||
|
|
||||||
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)
|
||||||
|
|
||||||
|
############## Setup #########################
|
||||||
|
|
||||||
|
function die {
|
||||||
|
local status_code="$1"
|
||||||
|
shift
|
||||||
|
(>&2 echo "${@}")
|
||||||
|
exit "$status_code"
|
||||||
|
}
|
||||||
|
|
||||||
|
function log {
|
||||||
|
(>&2 echo "${@}")
|
||||||
|
}
|
||||||
|
|
||||||
|
############## Program #########################
|
||||||
|
|
||||||
function main {
|
function main {
|
||||||
build_container
|
build_container
|
||||||
launch_container "${@}"
|
launch_container "${@}"
|
||||||
@@ -23,7 +39,6 @@ function build_container {
|
|||||||
|
|
||||||
function launch_container {
|
function launch_container {
|
||||||
local additional_flags=()
|
local additional_flags=()
|
||||||
local additional_args=()
|
|
||||||
local features=(compare)
|
local features=(compare)
|
||||||
|
|
||||||
if [ "$NO_COLOR" != "" ]; then
|
if [ "$NO_COLOR" != "" ]; then
|
||||||
@@ -37,11 +52,8 @@ function launch_container {
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
if [ "$SHELL" != "YES" ]; then
|
if [ "$SHELL" != "YES" ]; then
|
||||||
local features_joined=$(IFS=","; echo "${features[*]}")
|
|
||||||
additional_args+=(cargo run --bin compare --no-default-features --features "$features_joined")
|
|
||||||
additional_flags+=(--read-only)
|
additional_flags+=(--read-only)
|
||||||
else
|
else
|
||||||
additional_args+=(/bin/sh)
|
|
||||||
additional_flags+=(-t)
|
additional_flags+=(-t)
|
||||||
fi
|
fi
|
||||||
|
|
||||||
@@ -49,16 +61,50 @@ function launch_container {
|
|||||||
additional_flags+=(--env RUST_BACKTRACE=full)
|
additional_flags+=(--env RUST_BACKTRACE=full)
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
if [ "$SHELL" = "YES" ]; then
|
||||||
|
exec docker run "${additional_flags[@]}" --init --rm -i --mount type=tmpfs,destination=/tmp -v "/:/input:ro" -v "$($REALPATH "$DIR/../"):/source:ro" --mount source=cargo-cache,target=/usr/local/cargo/registry --mount source=rust-cache,target=/target --env CARGO_TARGET_DIR=/target -w /source --entrypoint "" organic-test /bin/sh
|
||||||
|
fi
|
||||||
|
|
||||||
|
local features_joined
|
||||||
|
features_joined=$(IFS=","; echo "${features[*]}")
|
||||||
|
|
||||||
|
local build_flags=()
|
||||||
|
if [ "$PROFILE" = "dev" ] || [ "$PROFILE" = "debug" ]; then
|
||||||
|
PROFILE="debug"
|
||||||
|
else
|
||||||
|
build_flags+=(--profile "$PROFILE")
|
||||||
|
fi
|
||||||
|
|
||||||
|
|
||||||
if [ $# -gt 0 ]; then
|
if [ $# -gt 0 ]; then
|
||||||
# If we passed in args, we need to forward them along
|
# If we passed in args, we need to forward them along
|
||||||
for path in "${@}"; do
|
for path in "${@}"; do
|
||||||
local full_path=$($REALPATH "$path")
|
local full_path
|
||||||
local containing_folder=$(dirname "$full_path")
|
full_path=$($REALPATH "$path")
|
||||||
local file_name=$(basename "$full_path")
|
init_script=$(cat <<EOF
|
||||||
docker run "${additional_flags[@]}" --init --rm -i --mount type=tmpfs,destination=/tmp -v "${containing_folder}:/input:ro" -v "$($REALPATH "$DIR/../"):/source:ro" --mount source=cargo-cache,target=/usr/local/cargo/registry --mount source=rust-cache,target=/target --env CARGO_TARGET_DIR=/target -w /source --entrypoint "" organic-test "${additional_args[@]}" -- "/input/$file_name"
|
set -euo pipefail
|
||||||
|
IFS=\$'\n\t'
|
||||||
|
|
||||||
|
cargo build --bin compare --no-default-features --features "$features_joined" ${build_flags[@]}
|
||||||
|
exec /target/${PROFILE}/compare "/input${full_path}"
|
||||||
|
EOF
|
||||||
|
)
|
||||||
|
docker run "${additional_flags[@]}" --init --rm -i --mount type=tmpfs,destination=/tmp -v "/:/input:ro" -v "$($REALPATH "$DIR/../"):/source:ro" --mount source=cargo-cache,target=/usr/local/cargo/registry --mount source=rust-cache,target=/target --env CARGO_TARGET_DIR=/target -w /source --entrypoint "" organic-test sh -c "$init_script"
|
||||||
done
|
done
|
||||||
else
|
else
|
||||||
docker run "${additional_flags[@]}" --init --rm -i --mount type=tmpfs,destination=/tmp -v "$($REALPATH "$DIR/../"):/source:ro" --mount source=cargo-cache,target=/usr/local/cargo/registry --mount source=rust-cache,target=/target --env CARGO_TARGET_DIR=/target -w /source --entrypoint "" organic-test "${additional_args[@]}"
|
local current_directory init_script
|
||||||
|
current_directory=$(pwd)
|
||||||
|
init_script=$(cat <<EOF
|
||||||
|
set -euo pipefail
|
||||||
|
IFS=\$'\n\t'
|
||||||
|
|
||||||
|
cargo build --bin compare --no-default-features --features "$features_joined" ${build_flags[@]}
|
||||||
|
cd /input${current_directory}
|
||||||
|
exec /target/${PROFILE}/compare
|
||||||
|
EOF
|
||||||
|
)
|
||||||
|
|
||||||
|
docker run "${additional_flags[@]}" --init --rm -i --mount type=tmpfs,destination=/tmp -v "/:/input:ro" -v "$($REALPATH "$DIR/../"):/source:ro" --mount source=cargo-cache,target=/usr/local/cargo/registry --mount source=rust-cache,target=/target --env CARGO_TARGET_DIR=/target -w /source --entrypoint "" organic-test sh -c "$init_script"
|
||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -9,17 +9,6 @@ REALPATH=$(command -v uu-realpath || command -v realpath)
|
|||||||
|
|
||||||
############## Setup #########################
|
############## Setup #########################
|
||||||
|
|
||||||
function cleanup {
|
|
||||||
for f in "${folders[@]}"; do
|
|
||||||
log "Deleting $f"
|
|
||||||
rm -rf "$f"
|
|
||||||
done
|
|
||||||
}
|
|
||||||
folders=()
|
|
||||||
for sig in EXIT INT QUIT HUP TERM; do
|
|
||||||
trap "set +e; cleanup" "$sig"
|
|
||||||
done
|
|
||||||
|
|
||||||
function die {
|
function die {
|
||||||
local status_code="$1"
|
local status_code="$1"
|
||||||
shift
|
shift
|
||||||
@@ -34,18 +23,18 @@ function log {
|
|||||||
############## Program #########################
|
############## Program #########################
|
||||||
|
|
||||||
function main {
|
function main {
|
||||||
log "Is is recommended that the output of \`mktemp -d -t 'compare_bisect.XXXXXXXX'\` is inside a tmpfs filesystem since this script will make many writes to these folders."
|
local target_full_path
|
||||||
|
target_full_path=$($REALPATH "$1")
|
||||||
local target_full_path=$($REALPATH "$1")
|
|
||||||
SOURCE_FOLDER=$(dirname "$target_full_path")
|
SOURCE_FOLDER=$(dirname "$target_full_path")
|
||||||
TARGET_DOCUMENT=$(basename "$target_full_path")
|
TARGET_DOCUMENT=$(basename "$target_full_path")
|
||||||
|
|
||||||
|
|
||||||
local good=0
|
local good=0
|
||||||
local bad=$(wc -l "$SOURCE_FOLDER/$TARGET_DOCUMENT" | awk '{print $1}')
|
local bad
|
||||||
|
bad=$(wc -l "$SOURCE_FOLDER/$TARGET_DOCUMENT" | awk '{print $1}')
|
||||||
|
|
||||||
set +e
|
set +e
|
||||||
run_parse "$bad" &> /dev/null
|
(run_parse "$bad")
|
||||||
local status=$?
|
local status=$?
|
||||||
set -e
|
set -e
|
||||||
if [ $status -eq 0 ]; then
|
if [ $status -eq 0 ]; then
|
||||||
@@ -71,21 +60,12 @@ function main {
|
|||||||
echo "Bad line: $bad"
|
echo "Bad line: $bad"
|
||||||
}
|
}
|
||||||
|
|
||||||
function setup_temp_dir {
|
|
||||||
local temp_dir=$(mktemp -d -t 'compare_bisect.XXXXXXXX')
|
|
||||||
cp -r "$SOURCE_FOLDER/"* "$temp_dir/"
|
|
||||||
echo "$temp_dir"
|
|
||||||
}
|
|
||||||
|
|
||||||
function run_parse {
|
function run_parse {
|
||||||
local lines="$1"
|
local lines="$1"
|
||||||
local temp_dir=$(setup_temp_dir)
|
|
||||||
folders+=("$temp_dir")
|
cd "$SOURCE_FOLDER"
|
||||||
cat "$SOURCE_FOLDER/$TARGET_DOCUMENT" | head -n "$lines" > "$temp_dir/$TARGET_DOCUMENT"
|
head -n "$lines" "$SOURCE_FOLDER/$TARGET_DOCUMENT" | PROFILE=release-lto "${DIR}/run_docker_compare.bash"
|
||||||
"${DIR}/run_docker_compare.bash" "$temp_dir/$TARGET_DOCUMENT"
|
|
||||||
local status=$?
|
local status=$?
|
||||||
rm -rf "$temp_dir"
|
|
||||||
# TODO: Remove temp_dir from folders
|
|
||||||
return "$status"
|
return "$status"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -490,11 +490,11 @@ fn compare_heading<'s>(
|
|||||||
let level = get_property(emacs, ":level")?
|
let level = get_property(emacs, ":level")?
|
||||||
.ok_or("Level should not be nil")?
|
.ok_or("Level should not be nil")?
|
||||||
.as_atom()?;
|
.as_atom()?;
|
||||||
if rust.stars.to_string() != level {
|
if rust.level.to_string() != level {
|
||||||
this_status = DiffStatus::Bad;
|
this_status = DiffStatus::Bad;
|
||||||
message = Some(format!(
|
message = Some(format!(
|
||||||
"Headline level do not match (emacs != rust): {} != {}",
|
"Headline level do not match (emacs != rust): {} != {}",
|
||||||
level, rust.stars
|
level, rust.level
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -732,6 +732,10 @@ fn compare_plain_list<'s>(
|
|||||||
Ok(_) => {}
|
Ok(_) => {}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// TODO compare :type
|
||||||
|
//
|
||||||
|
// :type is an unquoted atom of either descriptive, ordered, or unordered
|
||||||
|
|
||||||
for (emacs_child, rust_child) in children.iter().skip(2).zip(rust.children.iter()) {
|
for (emacs_child, rust_child) in children.iter().skip(2).zip(rust.children.iter()) {
|
||||||
child_status.push(compare_plain_list_item(source, emacs_child, rust_child)?);
|
child_status.push(compare_plain_list_item(source, emacs_child, rust_child)?);
|
||||||
}
|
}
|
||||||
@@ -894,6 +898,8 @@ fn compare_dynamic_block<'s>(
|
|||||||
Ok(_) => {}
|
Ok(_) => {}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// TODO: Compare :block-name :arguments
|
||||||
|
|
||||||
for (emacs_child, rust_child) in children.iter().skip(2).zip(rust.children.iter()) {
|
for (emacs_child, rust_child) in children.iter().skip(2).zip(rust.children.iter()) {
|
||||||
child_status.push(compare_element(source, emacs_child, rust_child)?);
|
child_status.push(compare_element(source, emacs_child, rust_child)?);
|
||||||
}
|
}
|
||||||
@@ -2475,6 +2481,8 @@ fn compare_subscript<'s>(
|
|||||||
Ok(_) => {}
|
Ok(_) => {}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// TODO compare :use-brackets-p
|
||||||
|
|
||||||
Ok(DiffResult {
|
Ok(DiffResult {
|
||||||
status: this_status,
|
status: this_status,
|
||||||
name: emacs_name.to_owned(),
|
name: emacs_name.to_owned(),
|
||||||
@@ -2506,6 +2514,8 @@ fn compare_superscript<'s>(
|
|||||||
Ok(_) => {}
|
Ok(_) => {}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// TODO compare :use-brackets-p
|
||||||
|
|
||||||
Ok(DiffResult {
|
Ok(DiffResult {
|
||||||
status: this_status,
|
status: this_status,
|
||||||
name: emacs_name.to_owned(),
|
name: emacs_name.to_owned(),
|
||||||
|
|||||||
@@ -22,6 +22,11 @@ pub struct GlobalSettings<'g, 's> {
|
|||||||
///
|
///
|
||||||
/// Corresponds to the tab-width elisp variable.
|
/// Corresponds to the tab-width elisp variable.
|
||||||
pub tab_width: IndentationLevel,
|
pub tab_width: IndentationLevel,
|
||||||
|
|
||||||
|
/// Whether to only allow odd headline levels.
|
||||||
|
///
|
||||||
|
/// Corresponds to org-odd-levels-only elisp variable.
|
||||||
|
pub odd_levels_only: HeadlineLevelFilter,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'g, 's> GlobalSettings<'g, 's> {
|
impl<'g, 's> GlobalSettings<'g, 's> {
|
||||||
@@ -35,6 +40,7 @@ impl<'g, 's> GlobalSettings<'g, 's> {
|
|||||||
complete_todo_keywords: BTreeSet::new(),
|
complete_todo_keywords: BTreeSet::new(),
|
||||||
org_list_allow_alphabetical: false,
|
org_list_allow_alphabetical: false,
|
||||||
tab_width: 8,
|
tab_width: 8,
|
||||||
|
odd_levels_only: HeadlineLevelFilter::OddEven,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -44,3 +50,9 @@ impl<'g, 's> Default for GlobalSettings<'g, 's> {
|
|||||||
GlobalSettings::new()
|
GlobalSettings::new()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub enum HeadlineLevelFilter {
|
||||||
|
Odd,
|
||||||
|
OddEven,
|
||||||
|
}
|
||||||
|
|||||||
@@ -25,5 +25,6 @@ pub(crate) use exiting::ExitClass;
|
|||||||
pub use file_access_interface::FileAccessInterface;
|
pub use file_access_interface::FileAccessInterface;
|
||||||
pub use file_access_interface::LocalFileAccessInterface;
|
pub use file_access_interface::LocalFileAccessInterface;
|
||||||
pub use global_settings::GlobalSettings;
|
pub use global_settings::GlobalSettings;
|
||||||
|
pub use global_settings::HeadlineLevelFilter;
|
||||||
pub(crate) use list::List;
|
pub(crate) use list::List;
|
||||||
pub(crate) use parser_with_context::parser_with_context;
|
pub(crate) use parser_with_context::parser_with_context;
|
||||||
|
|||||||
@@ -1,14 +1,18 @@
|
|||||||
use nom::branch::alt;
|
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_no_case;
|
use nom::bytes::complete::tag_no_case;
|
||||||
use nom::character::complete::line_ending;
|
use nom::character::complete::line_ending;
|
||||||
use nom::character::complete::space0;
|
use nom::character::complete::space0;
|
||||||
use nom::character::complete::space1;
|
use nom::character::complete::space1;
|
||||||
|
use nom::combinator::consumed;
|
||||||
use nom::combinator::eof;
|
use nom::combinator::eof;
|
||||||
use nom::combinator::not;
|
use nom::combinator::not;
|
||||||
use nom::combinator::opt;
|
use nom::combinator::opt;
|
||||||
use nom::combinator::recognize;
|
use nom::combinator::recognize;
|
||||||
|
use nom::multi::many0;
|
||||||
use nom::multi::many_till;
|
use nom::multi::many_till;
|
||||||
|
use nom::sequence::preceded;
|
||||||
use nom::sequence::tuple;
|
use nom::sequence::tuple;
|
||||||
|
|
||||||
use super::org_source::OrgSource;
|
use super::org_source::OrgSource;
|
||||||
@@ -67,24 +71,23 @@ pub(crate) fn dynamic_block<'b, 'g, 'r, 's>(
|
|||||||
};
|
};
|
||||||
let element_matcher = parser_with_context!(element(true))(&parser_context);
|
let element_matcher = parser_with_context!(element(true))(&parser_context);
|
||||||
let exit_matcher = parser_with_context!(exit_matcher_parser)(&parser_context);
|
let exit_matcher = parser_with_context!(exit_matcher_parser)(&parser_context);
|
||||||
let (remaining, children) = match tuple((
|
not(exit_matcher)(remaining)?;
|
||||||
not(exit_matcher),
|
let (remaining, leading_blank_lines) = opt(consumed(tuple((
|
||||||
blank_line,
|
blank_line,
|
||||||
many_till(blank_line, exit_matcher),
|
many0(preceded(not(exit_matcher), blank_line)),
|
||||||
))(remaining)
|
))))(remaining)?;
|
||||||
{
|
let leading_blank_lines =
|
||||||
Ok((remain, (_not_immediate_exit, first_line, (_trailing_whitespace, _exit_contents)))) => {
|
leading_blank_lines.map(|(source, (first_line, _remaining_lines))| {
|
||||||
let mut element = Element::Paragraph(Paragraph::of_text(first_line.into()));
|
let mut element = Element::Paragraph(Paragraph::of_text(first_line.into()));
|
||||||
let source = get_consumed(remaining, remain);
|
|
||||||
element.set_source(source.into());
|
element.set_source(source.into());
|
||||||
(remain, vec![element])
|
element
|
||||||
}
|
});
|
||||||
Err(_) => {
|
let (remaining, (mut children, _exit_contents)) =
|
||||||
let (remaining, (children, _exit_contents)) =
|
many_till(element_matcher, exit_matcher)(remaining)?;
|
||||||
many_till(element_matcher, exit_matcher)(remaining)?;
|
if let Some(lines) = leading_blank_lines {
|
||||||
(remaining, children)
|
children.insert(0, lines);
|
||||||
}
|
}
|
||||||
};
|
|
||||||
let (remaining, _end) = dynamic_block_end(&parser_context, remaining)?;
|
let (remaining, _end) = dynamic_block_end(&parser_context, remaining)?;
|
||||||
|
|
||||||
let source = get_consumed(input, remaining);
|
let source = get_consumed(input, remaining);
|
||||||
@@ -117,7 +120,8 @@ fn dynamic_block_end<'b, 'g, 'r, 's>(
|
|||||||
start_of_line(input)?;
|
start_of_line(input)?;
|
||||||
let (remaining, source) = recognize(tuple((
|
let (remaining, source) = recognize(tuple((
|
||||||
space0,
|
space0,
|
||||||
tag_no_case("#+end:"),
|
tag_no_case("#+end"),
|
||||||
|
opt(tag(":")),
|
||||||
alt((eof, line_ending)),
|
alt((eof, line_ending)),
|
||||||
)))(input)?;
|
)))(input)?;
|
||||||
Ok((remaining, source))
|
Ok((remaining, source))
|
||||||
|
|||||||
@@ -3,7 +3,6 @@ use nom::bytes::complete::is_not;
|
|||||||
use nom::bytes::complete::tag;
|
use nom::bytes::complete::tag;
|
||||||
use nom::character::complete::line_ending;
|
use nom::character::complete::line_ending;
|
||||||
use nom::character::complete::space0;
|
use nom::character::complete::space0;
|
||||||
use nom::character::complete::space1;
|
|
||||||
use nom::combinator::eof;
|
use nom::combinator::eof;
|
||||||
use nom::combinator::not;
|
use nom::combinator::not;
|
||||||
use nom::combinator::recognize;
|
use nom::combinator::recognize;
|
||||||
@@ -12,6 +11,7 @@ use nom::sequence::preceded;
|
|||||||
use nom::sequence::tuple;
|
use nom::sequence::tuple;
|
||||||
|
|
||||||
use super::org_source::OrgSource;
|
use super::org_source::OrgSource;
|
||||||
|
use super::util::only_space1;
|
||||||
use super::util::org_line_ending;
|
use super::util::org_line_ending;
|
||||||
use crate::context::parser_with_context;
|
use crate::context::parser_with_context;
|
||||||
use crate::context::RefContext;
|
use crate::context::RefContext;
|
||||||
@@ -50,7 +50,7 @@ fn fixed_width_area_line<'b, 'g, 'r, 's>(
|
|||||||
let (remaining, _indent) = space0(input)?;
|
let (remaining, _indent) = space0(input)?;
|
||||||
let (remaining, _) = tuple((
|
let (remaining, _) = tuple((
|
||||||
tag(":"),
|
tag(":"),
|
||||||
alt((recognize(tuple((space1, is_not("\r\n")))), space0)),
|
alt((recognize(tuple((only_space1, is_not("\r\n")))), space0)),
|
||||||
org_line_ending,
|
org_line_ending,
|
||||||
))(remaining)?;
|
))(remaining)?;
|
||||||
let source = get_consumed(input, remaining);
|
let source = get_consumed(input, remaining);
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
use nom::branch::alt;
|
use nom::branch::alt;
|
||||||
|
use nom::bytes::complete::is_a;
|
||||||
use nom::bytes::complete::tag;
|
use nom::bytes::complete::tag;
|
||||||
use nom::character::complete::anychar;
|
use nom::character::complete::anychar;
|
||||||
use nom::character::complete::space0;
|
use nom::character::complete::space0;
|
||||||
@@ -11,7 +12,6 @@ use nom::combinator::recognize;
|
|||||||
use nom::combinator::verify;
|
use nom::combinator::verify;
|
||||||
use nom::multi::many0;
|
use nom::multi::many0;
|
||||||
use nom::multi::many1;
|
use nom::multi::many1;
|
||||||
use nom::multi::many1_count;
|
|
||||||
use nom::multi::separated_list1;
|
use nom::multi::separated_list1;
|
||||||
use nom::sequence::tuple;
|
use nom::sequence::tuple;
|
||||||
|
|
||||||
@@ -34,30 +34,39 @@ use crate::parser::object_parser::standard_set_object;
|
|||||||
use crate::parser::util::blank_line;
|
use crate::parser::util::blank_line;
|
||||||
use crate::types::DocumentElement;
|
use crate::types::DocumentElement;
|
||||||
use crate::types::Heading;
|
use crate::types::Heading;
|
||||||
|
use crate::types::HeadlineLevel;
|
||||||
use crate::types::Object;
|
use crate::types::Object;
|
||||||
use crate::types::PriorityCookie;
|
use crate::types::PriorityCookie;
|
||||||
use crate::types::TodoKeywordType;
|
use crate::types::TodoKeywordType;
|
||||||
|
|
||||||
pub(crate) const fn heading(
|
pub(crate) const fn heading(
|
||||||
parent_stars: usize,
|
parent_level: HeadlineLevel,
|
||||||
) -> impl for<'b, 'g, 'r, 's> Fn(
|
) -> impl for<'b, 'g, 'r, 's> Fn(
|
||||||
RefContext<'b, 'g, 'r, 's>,
|
RefContext<'b, 'g, 'r, 's>,
|
||||||
OrgSource<'s>,
|
OrgSource<'s>,
|
||||||
) -> Res<OrgSource<'s>, Heading<'s>> {
|
) -> Res<OrgSource<'s>, Heading<'s>> {
|
||||||
move |context, input: OrgSource<'_>| _heading(context, input, parent_stars)
|
move |context, input: OrgSource<'_>| _heading(context, input, parent_level)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
|
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
|
||||||
fn _heading<'b, 'g, 'r, 's>(
|
fn _heading<'b, 'g, 'r, 's>(
|
||||||
context: RefContext<'b, 'g, 'r, 's>,
|
context: RefContext<'b, 'g, 'r, 's>,
|
||||||
input: OrgSource<'s>,
|
input: OrgSource<'s>,
|
||||||
parent_stars: usize,
|
parent_star_count: HeadlineLevel,
|
||||||
) -> 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 (
|
let (
|
||||||
remaining,
|
remaining,
|
||||||
(star_count, maybe_todo_keyword, maybe_priority, maybe_comment, title, heading_tags),
|
(
|
||||||
) = headline(context, input, parent_stars)?;
|
headline_level,
|
||||||
|
star_count,
|
||||||
|
maybe_todo_keyword,
|
||||||
|
maybe_priority,
|
||||||
|
maybe_comment,
|
||||||
|
title,
|
||||||
|
heading_tags,
|
||||||
|
),
|
||||||
|
) = headline(context, input, parent_star_count)?;
|
||||||
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(star_count))(context);
|
||||||
let (remaining, maybe_section) =
|
let (remaining, maybe_section) =
|
||||||
@@ -82,7 +91,7 @@ fn _heading<'b, 'g, 'r, 's>(
|
|||||||
remaining,
|
remaining,
|
||||||
Heading {
|
Heading {
|
||||||
source: source.into(),
|
source: source.into(),
|
||||||
stars: star_count,
|
level: headline_level,
|
||||||
todo_keyword: maybe_todo_keyword.map(|(todo_keyword_type, todo_keyword)| {
|
todo_keyword: maybe_todo_keyword.map(|(todo_keyword_type, todo_keyword)| {
|
||||||
(todo_keyword_type, Into::<&str>::into(todo_keyword))
|
(todo_keyword_type, Into::<&str>::into(todo_keyword))
|
||||||
}),
|
}),
|
||||||
@@ -106,11 +115,12 @@ pub(crate) fn detect_headline<'s>(input: OrgSource<'s>) -> Res<OrgSource<'s>, ()
|
|||||||
fn headline<'b, 'g, 'r, 's>(
|
fn headline<'b, 'g, 'r, 's>(
|
||||||
context: RefContext<'b, 'g, 'r, 's>,
|
context: RefContext<'b, 'g, 'r, 's>,
|
||||||
input: OrgSource<'s>,
|
input: OrgSource<'s>,
|
||||||
parent_stars: usize,
|
parent_star_count: HeadlineLevel,
|
||||||
) -> Res<
|
) -> Res<
|
||||||
OrgSource<'s>,
|
OrgSource<'s>,
|
||||||
(
|
(
|
||||||
usize,
|
HeadlineLevel,
|
||||||
|
HeadlineLevel,
|
||||||
Option<(TodoKeywordType, OrgSource<'s>)>,
|
Option<(TodoKeywordType, OrgSource<'s>)>,
|
||||||
Option<(OrgSource<'s>, PriorityCookie)>,
|
Option<(OrgSource<'s>, PriorityCookie)>,
|
||||||
Option<OrgSource<'s>>,
|
Option<OrgSource<'s>>,
|
||||||
@@ -124,11 +134,12 @@ fn headline<'b, 'g, 'r, 's>(
|
|||||||
});
|
});
|
||||||
let parser_context = context.with_additional_node(&parser_context);
|
let parser_context = context.with_additional_node(&parser_context);
|
||||||
|
|
||||||
let (remaining, (_, star_count, _)) = tuple((
|
let (remaining, (_, (headline_level, star_count, _), _)) = tuple((
|
||||||
start_of_line,
|
start_of_line,
|
||||||
verify(many1_count(tag("*")), |star_count| {
|
verify(
|
||||||
*star_count > parent_stars
|
parser_with_context!(headline_level)(&parser_context),
|
||||||
}),
|
|(_, count, _)| *count > parent_star_count,
|
||||||
|
),
|
||||||
peek(org_space),
|
peek(org_space),
|
||||||
))(input)?;
|
))(input)?;
|
||||||
|
|
||||||
@@ -158,6 +169,7 @@ fn headline<'b, 'g, 'r, 's>(
|
|||||||
Ok((
|
Ok((
|
||||||
remaining,
|
remaining,
|
||||||
(
|
(
|
||||||
|
headline_level,
|
||||||
star_count,
|
star_count,
|
||||||
maybe_todo_keyword.map(|(_, todo, _)| todo),
|
maybe_todo_keyword.map(|(_, todo, _)| todo),
|
||||||
maybe_priority,
|
maybe_priority,
|
||||||
@@ -255,3 +267,23 @@ fn priority_cookie<'s>(input: OrgSource<'s>) -> Res<OrgSource<'s>, PriorityCooki
|
|||||||
})?;
|
})?;
|
||||||
Ok((remaining, cookie))
|
Ok((remaining, cookie))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
|
||||||
|
fn headline_level<'b, 'g, 'r, 's>(
|
||||||
|
context: RefContext<'b, 'g, 'r, 's>,
|
||||||
|
input: OrgSource<'s>,
|
||||||
|
) -> Res<OrgSource<'s>, (HeadlineLevel, HeadlineLevel, OrgSource<'s>)> {
|
||||||
|
let (remaining, stars) = is_a("*")(input)?;
|
||||||
|
let count = stars.len().try_into().unwrap();
|
||||||
|
let level = match context.get_global_settings().odd_levels_only {
|
||||||
|
crate::context::HeadlineLevelFilter::Odd => {
|
||||||
|
if count % 2 == 0 {
|
||||||
|
(count + 2) / 2
|
||||||
|
} else {
|
||||||
|
(count + 1) / 2
|
||||||
|
}
|
||||||
|
}
|
||||||
|
crate::context::HeadlineLevelFilter::OddEven => count,
|
||||||
|
};
|
||||||
|
Ok((remaining, (level, count, stars)))
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,13 +1,17 @@
|
|||||||
use nom::branch::alt;
|
use nom::branch::alt;
|
||||||
|
use nom::bytes::complete::is_not;
|
||||||
use nom::bytes::complete::tag_no_case;
|
use nom::bytes::complete::tag_no_case;
|
||||||
use nom::character::complete::anychar;
|
use nom::character::complete::anychar;
|
||||||
|
use nom::character::complete::space1;
|
||||||
use nom::combinator::map;
|
use nom::combinator::map;
|
||||||
use nom::multi::many0;
|
use nom::multi::many0;
|
||||||
use nom::multi::many_till;
|
use nom::multi::many_till;
|
||||||
|
use nom::multi::separated_list0;
|
||||||
|
|
||||||
use super::keyword::filtered_keyword;
|
use super::keyword::filtered_keyword;
|
||||||
use super::keyword_todo::todo_keywords;
|
use super::keyword_todo::todo_keywords;
|
||||||
use super::OrgSource;
|
use super::OrgSource;
|
||||||
|
use crate::context::HeadlineLevelFilter;
|
||||||
use crate::error::Res;
|
use crate::error::Res;
|
||||||
use crate::types::Keyword;
|
use crate::types::Keyword;
|
||||||
use crate::GlobalSettings;
|
use crate::GlobalSettings;
|
||||||
@@ -50,6 +54,7 @@ pub(crate) fn apply_in_buffer_settings<'g, 's, 'sf>(
|
|||||||
) -> Result<GlobalSettings<'g, 's>, String> {
|
) -> Result<GlobalSettings<'g, 's>, String> {
|
||||||
let mut new_settings = original_settings.clone();
|
let mut new_settings = original_settings.clone();
|
||||||
|
|
||||||
|
// Todo Keywords
|
||||||
for kw in keywords.iter().filter(|kw| {
|
for kw in keywords.iter().filter(|kw| {
|
||||||
kw.key.eq_ignore_ascii_case("todo")
|
kw.key.eq_ignore_ascii_case("todo")
|
||||||
|| kw.key.eq_ignore_ascii_case("seq_todo")
|
|| kw.key.eq_ignore_ascii_case("seq_todo")
|
||||||
@@ -65,5 +70,21 @@ pub(crate) fn apply_in_buffer_settings<'g, 's, 'sf>(
|
|||||||
.extend(complete_words.into_iter().map(str::to_string));
|
.extend(complete_words.into_iter().map(str::to_string));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Startup settings
|
||||||
|
for kw in keywords
|
||||||
|
.iter()
|
||||||
|
.filter(|kw| kw.key.eq_ignore_ascii_case("startup"))
|
||||||
|
{
|
||||||
|
let (_remaining, settings) =
|
||||||
|
separated_list0(space1::<&str, nom::error::Error<_>>, is_not(" \t"))(kw.value)
|
||||||
|
.map_err(|err: nom::Err<_>| err.to_string())?;
|
||||||
|
if settings.contains(&"odd") {
|
||||||
|
new_settings.odd_levels_only = HeadlineLevelFilter::Odd;
|
||||||
|
}
|
||||||
|
if settings.contains(&"oddeven") {
|
||||||
|
new_settings.odd_levels_only = HeadlineLevelFilter::OddEven;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Ok(new_settings)
|
Ok(new_settings)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ 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::combinator::verify;
|
||||||
use nom::multi::many_till;
|
use nom::multi::many_till;
|
||||||
use nom::sequence::tuple;
|
use nom::sequence::tuple;
|
||||||
|
|
||||||
@@ -116,7 +117,9 @@ pub(crate) fn table_formula_keyword<'b, 'g, 'r, 's>(
|
|||||||
_context: RefContext<'b, 'g, 'r, 's>,
|
_context: RefContext<'b, 'g, 'r, 's>,
|
||||||
input: OrgSource<'s>,
|
input: OrgSource<'s>,
|
||||||
) -> Res<OrgSource<'s>, Keyword<'s>> {
|
) -> Res<OrgSource<'s>, Keyword<'s>> {
|
||||||
filtered_keyword(table_formula_key)(input)
|
verify(filtered_keyword(table_formula_key), |kw| {
|
||||||
|
!kw.value.is_empty()
|
||||||
|
})(input)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
|
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
|
||||||
|
|||||||
@@ -59,6 +59,10 @@ impl<'s> OrgSource<'s> {
|
|||||||
self.end - self.start
|
self.end - self.start
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(crate) fn get_byte_offset(&self) -> usize {
|
||||||
|
self.start
|
||||||
|
}
|
||||||
|
|
||||||
pub(crate) fn get_preceding_character(&self) -> Option<char> {
|
pub(crate) fn get_preceding_character(&self) -> Option<char> {
|
||||||
self.preceding_character
|
self.preceding_character
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,17 +5,24 @@ use nom::character::complete::anychar;
|
|||||||
use nom::character::complete::none_of;
|
use nom::character::complete::none_of;
|
||||||
use nom::character::complete::one_of;
|
use nom::character::complete::one_of;
|
||||||
use nom::combinator::eof;
|
use nom::combinator::eof;
|
||||||
|
use nom::combinator::not;
|
||||||
use nom::combinator::peek;
|
use nom::combinator::peek;
|
||||||
use nom::combinator::recognize;
|
use nom::combinator::recognize;
|
||||||
use nom::combinator::verify;
|
use nom::combinator::verify;
|
||||||
|
use nom::multi::many0;
|
||||||
|
use nom::multi::many1;
|
||||||
use nom::multi::many_till;
|
use nom::multi::many_till;
|
||||||
|
use nom::sequence::tuple;
|
||||||
|
|
||||||
|
use super::org_source::BracketDepth;
|
||||||
use super::org_source::OrgSource;
|
use super::org_source::OrgSource;
|
||||||
use super::util::maybe_consume_object_trailing_whitespace_if_not_exiting;
|
use super::util::maybe_consume_object_trailing_whitespace_if_not_exiting;
|
||||||
use crate::context::parser_with_context;
|
use crate::context::parser_with_context;
|
||||||
use crate::context::ContextElement;
|
use crate::context::ContextElement;
|
||||||
|
use crate::context::ContextMatcher;
|
||||||
use crate::context::ExitClass;
|
use crate::context::ExitClass;
|
||||||
use crate::context::ExitMatcherNode;
|
use crate::context::ExitMatcherNode;
|
||||||
|
use crate::context::Matcher;
|
||||||
use crate::context::RefContext;
|
use crate::context::RefContext;
|
||||||
use crate::error::CustomError;
|
use crate::error::CustomError;
|
||||||
use crate::error::MyError;
|
use crate::error::MyError;
|
||||||
@@ -130,17 +137,77 @@ fn path_plain<'b, 'g, 'r, 's>(
|
|||||||
context: RefContext<'b, 'g, 'r, 's>,
|
context: RefContext<'b, 'g, 'r, 's>,
|
||||||
input: OrgSource<'s>,
|
input: OrgSource<'s>,
|
||||||
) -> Res<OrgSource<'s>, OrgSource<'s>> {
|
) -> Res<OrgSource<'s>, OrgSource<'s>> {
|
||||||
// TODO: "optionally containing parenthesis-wrapped non-whitespace non-bracket substrings up to a depth of two. The string must end with either a non-punctation non-whitespace character, a forwards slash, or a parenthesis-wrapped substring"
|
let path_plain_end = path_plain_end(input.get_parenthesis_depth());
|
||||||
let parser_context = ContextElement::ExitMatcherNode(ExitMatcherNode {
|
let parser_context = ContextElement::ExitMatcherNode(ExitMatcherNode {
|
||||||
class: ExitClass::Gamma,
|
class: ExitClass::Gamma,
|
||||||
exit_matcher: &path_plain_end,
|
exit_matcher: &path_plain_end,
|
||||||
});
|
});
|
||||||
let parser_context = context.with_additional_node(&parser_context);
|
let parser_context = context.with_additional_node(&parser_context);
|
||||||
|
|
||||||
let exit_matcher = parser_with_context!(exit_matcher_parser)(&parser_context);
|
let (remaining, _components) = many1(alt((
|
||||||
|
parser_with_context!(path_plain_no_parenthesis)(&parser_context),
|
||||||
|
parser_with_context!(path_plain_parenthesis)(&parser_context),
|
||||||
|
)))(input)?;
|
||||||
|
|
||||||
|
let source = get_consumed(input, remaining);
|
||||||
|
Ok((remaining, source))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn path_plain_end(starting_parenthesis_depth: BracketDepth) -> impl ContextMatcher {
|
||||||
|
move |context, input: OrgSource<'_>| _path_plain_end(context, input, starting_parenthesis_depth)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
|
||||||
|
fn _path_plain_end<'b, 'g, 'r, 's>(
|
||||||
|
context: RefContext<'b, 'g, 'r, 's>,
|
||||||
|
input: OrgSource<'s>,
|
||||||
|
starting_parenthesis_depth: BracketDepth,
|
||||||
|
) -> Res<OrgSource<'s>, OrgSource<'s>> {
|
||||||
|
let (remaining, _leading_punctuation) = many0(verify(anychar, |c| {
|
||||||
|
!" \t\r\n[]<>()/".contains(*c) && c.is_ascii_punctuation()
|
||||||
|
}))(input)?;
|
||||||
|
|
||||||
|
let disallowed_character = recognize(one_of(" \t\r\n[]<>"))(remaining);
|
||||||
|
if disallowed_character.is_ok() {
|
||||||
|
return disallowed_character;
|
||||||
|
}
|
||||||
|
|
||||||
|
let current_depth = remaining.get_parenthesis_depth() - starting_parenthesis_depth;
|
||||||
|
if current_depth == 0 {
|
||||||
|
let close_parenthesis =
|
||||||
|
tag::<&str, OrgSource<'_>, CustomError<OrgSource<'_>>>(")")(remaining);
|
||||||
|
if close_parenthesis.is_ok() {
|
||||||
|
return close_parenthesis;
|
||||||
|
}
|
||||||
|
|
||||||
|
let open_parenthesis_without_match = recognize(tuple((
|
||||||
|
peek(tag("(")),
|
||||||
|
not(parser_with_context!(path_plain_parenthesis)(context)),
|
||||||
|
)))(remaining);
|
||||||
|
if open_parenthesis_without_match.is_ok() {
|
||||||
|
return open_parenthesis_without_match;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// many0 punctuation
|
||||||
|
Err(nom::Err::Error(CustomError::MyError(MyError(
|
||||||
|
"No path plain end".into(),
|
||||||
|
))))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
|
||||||
|
fn path_plain_no_parenthesis<'b, 'g, 'r, 's>(
|
||||||
|
context: RefContext<'b, 'g, 'r, 's>,
|
||||||
|
input: OrgSource<'s>,
|
||||||
|
) -> Res<OrgSource<'s>, OrgSource<'s>> {
|
||||||
let (remaining, path) = recognize(verify(
|
let (remaining, path) = recognize(verify(
|
||||||
many_till(anychar, peek(exit_matcher)),
|
many_till(
|
||||||
|
anychar,
|
||||||
|
alt((
|
||||||
|
peek(path_plain_no_parenthesis_disallowed_character),
|
||||||
|
parser_with_context!(exit_matcher_parser)(context),
|
||||||
|
)),
|
||||||
|
),
|
||||||
|(children, _exit_contents)| !children.is_empty(),
|
|(children, _exit_contents)| !children.is_empty(),
|
||||||
))(input)?;
|
))(input)?;
|
||||||
|
|
||||||
@@ -148,14 +215,65 @@ fn path_plain<'b, 'g, 'r, 's>(
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
|
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
|
||||||
fn path_plain_end<'b, 'g, 'r, 's>(
|
fn path_plain_no_parenthesis_disallowed_character<'s>(
|
||||||
_context: RefContext<'b, 'g, 'r, 's>,
|
|
||||||
input: OrgSource<'s>,
|
input: OrgSource<'s>,
|
||||||
) -> Res<OrgSource<'s>, OrgSource<'s>> {
|
) -> Res<OrgSource<'s>, OrgSource<'s>> {
|
||||||
recognize(many_till(
|
recognize(verify(anychar, |c| {
|
||||||
verify(anychar, |c| {
|
c.is_whitespace() || "()[]<>".contains(*c)
|
||||||
*c != '/' && (c.is_ascii_punctuation() || c.is_whitespace())
|
}))(input)
|
||||||
}),
|
}
|
||||||
one_of(" \t\r\n()[]<>"),
|
|
||||||
))(input)
|
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
|
||||||
|
fn path_plain_parenthesis<'b, 'g, 'r, 's>(
|
||||||
|
context: RefContext<'b, 'g, 'r, 's>,
|
||||||
|
input: OrgSource<'s>,
|
||||||
|
) -> Res<OrgSource<'s>, OrgSource<'s>> {
|
||||||
|
let (remaining, _opening) = tag("(")(input)?;
|
||||||
|
let starting_depth = remaining.get_parenthesis_depth();
|
||||||
|
|
||||||
|
let (remaining, _path) = recognize(verify(
|
||||||
|
many_till(
|
||||||
|
anychar,
|
||||||
|
alt((
|
||||||
|
peek(path_plain_parenthesis_end(starting_depth)),
|
||||||
|
parser_with_context!(exit_matcher_parser)(context),
|
||||||
|
)),
|
||||||
|
),
|
||||||
|
|(children, _exit_contents)| !children.is_empty(),
|
||||||
|
))(remaining)?;
|
||||||
|
let (remaining, _opening) = tag(")")(remaining)?;
|
||||||
|
let source = get_consumed(input, remaining);
|
||||||
|
|
||||||
|
Ok((remaining, source))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn path_plain_parenthesis_end(starting_parenthesis_depth: BracketDepth) -> impl Matcher {
|
||||||
|
move |input: OrgSource<'_>| _path_plain_parenthesis_end(input, starting_parenthesis_depth)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
|
||||||
|
fn _path_plain_parenthesis_end<'s>(
|
||||||
|
input: OrgSource<'s>,
|
||||||
|
starting_parenthesis_depth: BracketDepth,
|
||||||
|
) -> Res<OrgSource<'s>, OrgSource<'s>> {
|
||||||
|
let current_depth = input.get_parenthesis_depth() - starting_parenthesis_depth;
|
||||||
|
if current_depth < 0 {
|
||||||
|
// This shouldn't be possible because if depth is 0 then a closing parenthesis should end the link.
|
||||||
|
unreachable!("Exceeded plain link parenthesis depth.")
|
||||||
|
}
|
||||||
|
if current_depth == 0 {
|
||||||
|
let close_parenthesis = tag::<&str, OrgSource<'_>, CustomError<OrgSource<'_>>>(")")(input);
|
||||||
|
if close_parenthesis.is_ok() {
|
||||||
|
return close_parenthesis;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if current_depth == 1 {
|
||||||
|
let open_parenthesis = tag::<&str, OrgSource<'_>, CustomError<OrgSource<'_>>>("(")(input);
|
||||||
|
if open_parenthesis.is_ok() {
|
||||||
|
return open_parenthesis;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Err(nom::Err::Error(CustomError::MyError(MyError(
|
||||||
|
"No closing parenthesis".into(),
|
||||||
|
))))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -57,7 +57,7 @@ pub(crate) fn detect_plain_list<'b, 'g, 'r, 's>(
|
|||||||
parser_with_context!(bullet)(context),
|
parser_with_context!(bullet)(context),
|
||||||
alt((space1, line_ending, eof)),
|
alt((space1, line_ending, eof)),
|
||||||
)),
|
)),
|
||||||
|(_start, indent, bull, _after_whitespace)| {
|
|(_start, indent, (_bullet_type, bull), _after_whitespace)| {
|
||||||
Into::<&str>::into(bull) != "*" || indent.len() > 0
|
Into::<&str>::into(bull) != "*" || indent.len() > 0
|
||||||
},
|
},
|
||||||
)(input)
|
)(input)
|
||||||
@@ -151,9 +151,9 @@ fn plain_list_item<'b, 'g, 'r, 's>(
|
|||||||
) -> Res<OrgSource<'s>, PlainListItem<'s>> {
|
) -> Res<OrgSource<'s>, PlainListItem<'s>> {
|
||||||
start_of_line(input)?;
|
start_of_line(input)?;
|
||||||
let (remaining, (indent_level, _leading_whitespace)) = indentation_level(context, input)?;
|
let (remaining, (indent_level, _leading_whitespace)) = indentation_level(context, input)?;
|
||||||
let (remaining, bull) = verify(
|
let (remaining, (bullet_type, bull)) = verify(
|
||||||
parser_with_context!(bullet)(context),
|
parser_with_context!(bullet)(context),
|
||||||
|bull: &OrgSource<'_>| Into::<&str>::into(bull) != "*" || indent_level > 0,
|
|(_bullet_type, bull)| Into::<&str>::into(bull) != "*" || indent_level > 0,
|
||||||
)(remaining)?;
|
)(remaining)?;
|
||||||
|
|
||||||
let (remaining, _maybe_counter_set) = opt(tuple((
|
let (remaining, _maybe_counter_set) = opt(tuple((
|
||||||
@@ -165,15 +165,33 @@ fn plain_list_item<'b, 'g, 'r, 's>(
|
|||||||
|
|
||||||
let (remaining, maybe_checkbox) = opt(tuple((space1, item_checkbox)))(remaining)?;
|
let (remaining, maybe_checkbox) = opt(tuple((space1, item_checkbox)))(remaining)?;
|
||||||
|
|
||||||
let (remaining, maybe_tag) =
|
let (remaining, maybe_tag) = if let BulletType::Unordered = bullet_type {
|
||||||
opt(tuple((space1, parser_with_context!(item_tag)(context))))(remaining)?;
|
opt(tuple((space1, parser_with_context!(item_tag)(context))))(remaining)?
|
||||||
|
} else {
|
||||||
|
(remaining, None)
|
||||||
|
};
|
||||||
|
|
||||||
|
let exit_matcher = plain_list_item_end(indent_level);
|
||||||
|
let contexts = [
|
||||||
|
ContextElement::ConsumeTrailingWhitespace(true),
|
||||||
|
ContextElement::ExitMatcherNode(ExitMatcherNode {
|
||||||
|
class: ExitClass::Beta,
|
||||||
|
exit_matcher: &exit_matcher,
|
||||||
|
}),
|
||||||
|
];
|
||||||
|
let parser_context = context.with_additional_node(&contexts[0]);
|
||||||
|
let parser_context = parser_context.with_additional_node(&contexts[1]);
|
||||||
|
|
||||||
let maybe_contentless_item: Res<OrgSource<'_>, ()> = peek(parser_with_context!(
|
let maybe_contentless_item: Res<OrgSource<'_>, ()> = peek(parser_with_context!(
|
||||||
detect_contentless_item_contents
|
detect_contentless_item_contents
|
||||||
)(context))(remaining);
|
)(&parser_context))(remaining);
|
||||||
match maybe_contentless_item {
|
match maybe_contentless_item {
|
||||||
Ok((_rem, _ws)) => {
|
Ok((_rem, _ws)) => {
|
||||||
let (remaining, _trailing_ws) = opt(blank_line)(remaining)?;
|
let (remaining, _trailing_ws) = if context.should_consume_trailing_whitespace() {
|
||||||
|
recognize(alt((recognize(many1(blank_line)), eof)))(remaining)?
|
||||||
|
} else {
|
||||||
|
recognize(alt((blank_line, eof)))(remaining)?
|
||||||
|
};
|
||||||
let source = get_consumed(input, remaining);
|
let source = get_consumed(input, remaining);
|
||||||
return Ok((
|
return Ok((
|
||||||
remaining,
|
remaining,
|
||||||
@@ -191,17 +209,7 @@ fn plain_list_item<'b, 'g, 'r, 's>(
|
|||||||
}
|
}
|
||||||
Err(_) => {}
|
Err(_) => {}
|
||||||
};
|
};
|
||||||
let (remaining, _ws) = item_tag_post_gap(context, remaining)?;
|
let (remaining, _ws) = item_tag_post_gap(&parser_context, remaining)?;
|
||||||
let exit_matcher = plain_list_item_end(indent_level);
|
|
||||||
let contexts = [
|
|
||||||
ContextElement::ConsumeTrailingWhitespace(true),
|
|
||||||
ContextElement::ExitMatcherNode(ExitMatcherNode {
|
|
||||||
class: ExitClass::Beta,
|
|
||||||
exit_matcher: &exit_matcher,
|
|
||||||
}),
|
|
||||||
];
|
|
||||||
let parser_context = context.with_additional_node(&contexts[0]);
|
|
||||||
let parser_context = parser_context.with_additional_node(&contexts[1]);
|
|
||||||
|
|
||||||
let (mut remaining, (mut children, _exit_contents)) = many_till(
|
let (mut remaining, (mut children, _exit_contents)) = many_till(
|
||||||
include_input(parser_with_context!(element(true))(&parser_context)),
|
include_input(parser_with_context!(element(true))(&parser_context)),
|
||||||
@@ -241,19 +249,28 @@ fn plain_list_item<'b, 'g, 'r, 's>(
|
|||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
enum BulletType {
|
||||||
|
Ordered,
|
||||||
|
Unordered,
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
|
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
|
||||||
fn bullet<'b, 'g, 'r, 's>(
|
fn bullet<'b, 'g, 'r, 's>(
|
||||||
context: RefContext<'b, 'g, 'r, 's>,
|
context: RefContext<'b, 'g, 'r, 's>,
|
||||||
input: OrgSource<'s>,
|
input: OrgSource<'s>,
|
||||||
) -> Res<OrgSource<'s>, OrgSource<'s>> {
|
) -> Res<OrgSource<'s>, (BulletType, OrgSource<'s>)> {
|
||||||
alt((
|
alt((
|
||||||
tag("*"),
|
map(tag("*"), |bull| (BulletType::Unordered, bull)),
|
||||||
tag("-"),
|
map(tag("-"), |bull| (BulletType::Unordered, bull)),
|
||||||
tag("+"),
|
map(tag("+"), |bull| (BulletType::Unordered, bull)),
|
||||||
recognize(tuple((
|
map(
|
||||||
parser_with_context!(counter)(context),
|
recognize(tuple((
|
||||||
alt((tag("."), tag(")"))),
|
parser_with_context!(counter)(context),
|
||||||
))),
|
alt((tag("."), tag(")"))),
|
||||||
|
))),
|
||||||
|
|bull| (BulletType::Ordered, bull),
|
||||||
|
),
|
||||||
))(input)
|
))(input)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -359,22 +376,22 @@ fn item_tag_end<'b, 'g, 'r, 's>(
|
|||||||
_context: RefContext<'b, 'g, 'r, 's>,
|
_context: RefContext<'b, 'g, 'r, 's>,
|
||||||
input: OrgSource<'s>,
|
input: OrgSource<'s>,
|
||||||
) -> Res<OrgSource<'s>, OrgSource<'s>> {
|
) -> Res<OrgSource<'s>, OrgSource<'s>> {
|
||||||
alt((
|
alt((item_tag_divider, line_ending))(input)
|
||||||
recognize(tuple((
|
}
|
||||||
item_tag_divider,
|
|
||||||
|
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
|
||||||
|
fn item_tag_divider<'s>(input: OrgSource<'s>) -> Res<OrgSource<'s>, OrgSource<'s>> {
|
||||||
|
recognize(tuple((
|
||||||
|
one_of(" \t"),
|
||||||
|
tag("::"),
|
||||||
|
peek(tuple((
|
||||||
opt(tuple((
|
opt(tuple((
|
||||||
peek(one_of(" \t")),
|
peek(one_of(" \t")),
|
||||||
many_till(anychar, peek(alt((item_tag_divider, line_ending, eof)))),
|
many_till(anychar, peek(alt((item_tag_divider, line_ending, eof)))),
|
||||||
))),
|
))),
|
||||||
alt((line_ending, eof)),
|
alt((line_ending, eof)),
|
||||||
))),
|
))),
|
||||||
line_ending,
|
)))(input)
|
||||||
))(input)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
|
|
||||||
fn item_tag_divider<'s>(input: OrgSource<'s>) -> Res<OrgSource<'s>, OrgSource<'s>> {
|
|
||||||
recognize(tuple((one_of(" \t"), tag("::"))))(input)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
|
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
|
||||||
|
|||||||
@@ -4,11 +4,13 @@ use nom::bytes::complete::tag_no_case;
|
|||||||
use nom::character::complete::anychar;
|
use nom::character::complete::anychar;
|
||||||
use nom::character::complete::line_ending;
|
use nom::character::complete::line_ending;
|
||||||
use nom::character::complete::one_of;
|
use nom::character::complete::one_of;
|
||||||
|
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::combinator::verify;
|
||||||
use nom::multi::many1;
|
use nom::multi::many1;
|
||||||
use nom::multi::many_till;
|
use nom::multi::many_till;
|
||||||
|
use nom::sequence::tuple;
|
||||||
|
|
||||||
use super::org_source::OrgSource;
|
use super::org_source::OrgSource;
|
||||||
use super::radio_link::RematchObject;
|
use super::radio_link::RematchObject;
|
||||||
@@ -87,11 +89,17 @@ impl<'x> RematchObject<'x> for PlainText<'x> {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
// let is_whitespace = recognize(many1(org_space_or_line_ending))(input);
|
|
||||||
let is_not_whitespace = is_not::<&str, &str, CustomError<_>>(" \t\r\n")(goal);
|
let is_not_whitespace = is_not::<&str, &str, CustomError<_>>(" \t\r\n")(goal);
|
||||||
match is_not_whitespace {
|
match is_not_whitespace {
|
||||||
Ok((new_goal, payload)) => {
|
Ok((new_goal, payload)) => {
|
||||||
let (new_remaining, _) = tag_no_case(payload)(remaining)?;
|
let (new_remaining, _) = tuple((
|
||||||
|
tag_no_case(payload),
|
||||||
|
// TODO: Test to see what the REAL condition is. Checking for not-alphabetic works fine for now, but the real criteria might be something like the plain text exit matcher.
|
||||||
|
peek(alt((
|
||||||
|
recognize(verify(anychar, |c| !c.is_alphanumeric())),
|
||||||
|
eof,
|
||||||
|
))),
|
||||||
|
))(remaining)?;
|
||||||
remaining = new_remaining;
|
remaining = new_remaining;
|
||||||
goal = new_goal;
|
goal = new_goal;
|
||||||
continue;
|
continue;
|
||||||
|
|||||||
@@ -62,6 +62,22 @@ pub(crate) fn rematch_target<'x, 'b, 'g, 'r, 's>(
|
|||||||
remaining = new_remaining;
|
remaining = new_remaining;
|
||||||
new_matches.push(new_match);
|
new_matches.push(new_match);
|
||||||
}
|
}
|
||||||
|
Object::Italic(italic) => {
|
||||||
|
let (new_remaining, new_match) = italic.rematch_object(context, remaining)?;
|
||||||
|
remaining = new_remaining;
|
||||||
|
new_matches.push(new_match);
|
||||||
|
}
|
||||||
|
Object::Underline(underline) => {
|
||||||
|
let (new_remaining, new_match) = underline.rematch_object(context, remaining)?;
|
||||||
|
remaining = new_remaining;
|
||||||
|
new_matches.push(new_match);
|
||||||
|
}
|
||||||
|
Object::StrikeThrough(strikethrough) => {
|
||||||
|
let (new_remaining, new_match) =
|
||||||
|
strikethrough.rematch_object(context, remaining)?;
|
||||||
|
remaining = new_remaining;
|
||||||
|
new_matches.push(new_match);
|
||||||
|
}
|
||||||
Object::PlainText(plaintext) => {
|
Object::PlainText(plaintext) => {
|
||||||
let (new_remaining, new_match) = plaintext.rematch_object(context, remaining)?;
|
let (new_remaining, new_match) = plaintext.rematch_object(context, remaining)?;
|
||||||
remaining = new_remaining;
|
remaining = new_remaining;
|
||||||
|
|||||||
@@ -78,7 +78,7 @@ fn pathreg<'b, 'g, 'r, 's>(
|
|||||||
) -> Res<OrgSource<'s>, OrgSource<'s>> {
|
) -> Res<OrgSource<'s>, OrgSource<'s>> {
|
||||||
let (remaining, path) = escaped(
|
let (remaining, path) = escaped(
|
||||||
take_till1(|c| match c {
|
take_till1(|c| match c {
|
||||||
'\\' | ']' => true,
|
'\\' | '[' | ']' => true,
|
||||||
_ => false,
|
_ => false,
|
||||||
}),
|
}),
|
||||||
'\\',
|
'\\',
|
||||||
|
|||||||
@@ -23,6 +23,7 @@ use crate::context::ContextElement;
|
|||||||
use crate::context::ContextMatcher;
|
use crate::context::ContextMatcher;
|
||||||
use crate::context::ExitClass;
|
use crate::context::ExitClass;
|
||||||
use crate::context::ExitMatcherNode;
|
use crate::context::ExitMatcherNode;
|
||||||
|
use crate::context::Matcher;
|
||||||
use crate::context::RefContext;
|
use crate::context::RefContext;
|
||||||
use crate::error::CustomError;
|
use crate::error::CustomError;
|
||||||
use crate::error::MyError;
|
use crate::error::MyError;
|
||||||
@@ -112,6 +113,10 @@ fn script_body<'b, 'g, 'r, 's>(
|
|||||||
map(parser_with_context!(script_with_braces)(context), |body| {
|
map(parser_with_context!(script_with_braces)(context), |body| {
|
||||||
ScriptBody::WithBraces(body.into())
|
ScriptBody::WithBraces(body.into())
|
||||||
}),
|
}),
|
||||||
|
map(
|
||||||
|
parser_with_context!(script_with_parenthesis)(context),
|
||||||
|
|body| ScriptBody::Braceless(body.into()),
|
||||||
|
),
|
||||||
))(input)
|
))(input)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -199,3 +204,49 @@ fn _script_with_braces_end<'b, 'g, 'r, 's>(
|
|||||||
}
|
}
|
||||||
tag("}")(input)
|
tag("}")(input)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
|
||||||
|
fn script_with_parenthesis<'b, 'g, 'r, 's>(
|
||||||
|
context: RefContext<'b, 'g, 'r, 's>,
|
||||||
|
input: OrgSource<'s>,
|
||||||
|
) -> Res<OrgSource<'s>, OrgSource<'s>> {
|
||||||
|
let (remaining, _) = tag("(")(input)?;
|
||||||
|
let exit_with_depth = script_with_parenthesis_end(remaining.get_parenthesis_depth());
|
||||||
|
|
||||||
|
let (remaining, _) = many_till(
|
||||||
|
anychar,
|
||||||
|
alt((
|
||||||
|
peek(exit_with_depth),
|
||||||
|
parser_with_context!(exit_matcher_parser)(context),
|
||||||
|
)),
|
||||||
|
)(remaining)?;
|
||||||
|
|
||||||
|
let (remaining, _) = tag(")")(remaining)?;
|
||||||
|
let source = get_consumed(input, remaining);
|
||||||
|
Ok((remaining, source))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn script_with_parenthesis_end(starting_parenthesis_depth: BracketDepth) -> impl Matcher {
|
||||||
|
move |input: OrgSource<'_>| _script_with_parenthesis_end(input, starting_parenthesis_depth)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
|
||||||
|
fn _script_with_parenthesis_end<'s>(
|
||||||
|
input: OrgSource<'s>,
|
||||||
|
starting_parenthesis_depth: BracketDepth,
|
||||||
|
) -> Res<OrgSource<'s>, OrgSource<'s>> {
|
||||||
|
let current_depth = input.get_parenthesis_depth() - starting_parenthesis_depth;
|
||||||
|
if current_depth < 0 {
|
||||||
|
// This shouldn't be possible because if depth is 0 then a closing bracket should end the citation.
|
||||||
|
unreachable!("Exceeded citation key suffix bracket depth.")
|
||||||
|
}
|
||||||
|
if current_depth == 0 {
|
||||||
|
let close_parenthesis = tag::<&str, OrgSource<'_>, CustomError<OrgSource<'_>>>(")")(input);
|
||||||
|
if close_parenthesis.is_ok() {
|
||||||
|
return close_parenthesis;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Err(nom::Err::Error(CustomError::MyError(MyError(
|
||||||
|
"No script parenthesis end.".into(),
|
||||||
|
))))
|
||||||
|
}
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ use super::org_source::OrgSource;
|
|||||||
use super::radio_link::RematchObject;
|
use super::radio_link::RematchObject;
|
||||||
use super::util::in_object_section;
|
use super::util::in_object_section;
|
||||||
use super::util::maybe_consume_object_trailing_whitespace_if_not_exiting;
|
use super::util::maybe_consume_object_trailing_whitespace_if_not_exiting;
|
||||||
|
use super::util::start_of_line;
|
||||||
use crate::context::parser_with_context;
|
use crate::context::parser_with_context;
|
||||||
use crate::context::ContextElement;
|
use crate::context::ContextElement;
|
||||||
use crate::context::ContextMatcher;
|
use crate::context::ContextMatcher;
|
||||||
@@ -64,8 +65,7 @@ fn bold<'b, 'g, 'r, 's>(
|
|||||||
context: RefContext<'b, 'g, 'r, 's>,
|
context: RefContext<'b, 'g, 'r, 's>,
|
||||||
input: OrgSource<'s>,
|
input: OrgSource<'s>,
|
||||||
) -> Res<OrgSource<'s>, Bold<'s>> {
|
) -> Res<OrgSource<'s>, Bold<'s>> {
|
||||||
let text_markup_object_specialized = text_markup_object("*");
|
let (remaining, children) = text_markup_object("*")(context, input)?;
|
||||||
let (remaining, children) = text_markup_object_specialized(context, input)?;
|
|
||||||
let source = get_consumed(input, remaining);
|
let source = get_consumed(input, remaining);
|
||||||
Ok((
|
Ok((
|
||||||
remaining,
|
remaining,
|
||||||
@@ -81,8 +81,7 @@ fn italic<'b, 'g, 'r, 's>(
|
|||||||
context: RefContext<'b, 'g, 'r, 's>,
|
context: RefContext<'b, 'g, 'r, 's>,
|
||||||
input: OrgSource<'s>,
|
input: OrgSource<'s>,
|
||||||
) -> Res<OrgSource<'s>, Italic<'s>> {
|
) -> Res<OrgSource<'s>, Italic<'s>> {
|
||||||
let text_markup_object_specialized = text_markup_object("/");
|
let (remaining, children) = text_markup_object("/")(context, input)?;
|
||||||
let (remaining, children) = text_markup_object_specialized(context, input)?;
|
|
||||||
let source = get_consumed(input, remaining);
|
let source = get_consumed(input, remaining);
|
||||||
Ok((
|
Ok((
|
||||||
remaining,
|
remaining,
|
||||||
@@ -98,8 +97,7 @@ fn underline<'b, 'g, 'r, 's>(
|
|||||||
context: RefContext<'b, 'g, 'r, 's>,
|
context: RefContext<'b, 'g, 'r, 's>,
|
||||||
input: OrgSource<'s>,
|
input: OrgSource<'s>,
|
||||||
) -> Res<OrgSource<'s>, Underline<'s>> {
|
) -> Res<OrgSource<'s>, Underline<'s>> {
|
||||||
let text_markup_object_specialized = text_markup_object("_");
|
let (remaining, children) = text_markup_object("_")(context, input)?;
|
||||||
let (remaining, children) = text_markup_object_specialized(context, input)?;
|
|
||||||
let source = get_consumed(input, remaining);
|
let source = get_consumed(input, remaining);
|
||||||
Ok((
|
Ok((
|
||||||
remaining,
|
remaining,
|
||||||
@@ -115,8 +113,7 @@ fn strike_through<'b, 'g, 'r, 's>(
|
|||||||
context: RefContext<'b, 'g, 'r, 's>,
|
context: RefContext<'b, 'g, 'r, 's>,
|
||||||
input: OrgSource<'s>,
|
input: OrgSource<'s>,
|
||||||
) -> Res<OrgSource<'s>, StrikeThrough<'s>> {
|
) -> Res<OrgSource<'s>, StrikeThrough<'s>> {
|
||||||
let text_markup_object_specialized = text_markup_object("+");
|
let (remaining, children) = text_markup_object("+")(context, input)?;
|
||||||
let (remaining, children) = text_markup_object_specialized(context, input)?;
|
|
||||||
let source = get_consumed(input, remaining);
|
let source = get_consumed(input, remaining);
|
||||||
Ok((
|
Ok((
|
||||||
remaining,
|
remaining,
|
||||||
@@ -132,8 +129,7 @@ fn verbatim<'b, 'g, 'r, 's>(
|
|||||||
context: RefContext<'b, 'g, 'r, 's>,
|
context: RefContext<'b, 'g, 'r, 's>,
|
||||||
input: OrgSource<'s>,
|
input: OrgSource<'s>,
|
||||||
) -> Res<OrgSource<'s>, Verbatim<'s>> {
|
) -> Res<OrgSource<'s>, Verbatim<'s>> {
|
||||||
let text_markup_string_specialized = text_markup_string("=");
|
let (remaining, contents) = text_markup_string("=")(context, input)?;
|
||||||
let (remaining, contents) = text_markup_string_specialized(context, input)?;
|
|
||||||
let source = get_consumed(input, remaining);
|
let source = get_consumed(input, remaining);
|
||||||
Ok((
|
Ok((
|
||||||
remaining,
|
remaining,
|
||||||
@@ -149,8 +145,7 @@ fn code<'b, 'g, 'r, 's>(
|
|||||||
context: RefContext<'b, 'g, 'r, 's>,
|
context: RefContext<'b, 'g, 'r, 's>,
|
||||||
input: OrgSource<'s>,
|
input: OrgSource<'s>,
|
||||||
) -> Res<OrgSource<'s>, Code<'s>> {
|
) -> Res<OrgSource<'s>, Code<'s>> {
|
||||||
let text_markup_string_specialized = text_markup_string("~");
|
let (remaining, contents) = text_markup_string("~")(context, input)?;
|
||||||
let (remaining, contents) = text_markup_string_specialized(context, input)?;
|
|
||||||
let source = get_consumed(input, remaining);
|
let source = get_consumed(input, remaining);
|
||||||
Ok((
|
Ok((
|
||||||
remaining,
|
remaining,
|
||||||
@@ -168,8 +163,7 @@ fn text_markup_object<'c>(
|
|||||||
OrgSource<'s>,
|
OrgSource<'s>,
|
||||||
) -> Res<OrgSource<'s>, Vec<Object<'s>>>
|
) -> Res<OrgSource<'s>, Vec<Object<'s>>>
|
||||||
+ 'c {
|
+ 'c {
|
||||||
let marker_symbol = marker_symbol.to_owned();
|
move |context, input: OrgSource<'_>| _text_markup_object(context, input, marker_symbol)
|
||||||
move |context, input: OrgSource<'_>| _text_markup_object(context, input, marker_symbol.as_str())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
|
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
|
||||||
@@ -188,7 +182,7 @@ fn _text_markup_object<'b, 'g, 'r, 's, 'c>(
|
|||||||
let (remaining, open) = tag(marker_symbol)(remaining)?;
|
let (remaining, open) = tag(marker_symbol)(remaining)?;
|
||||||
let (remaining, _peek_not_whitespace) =
|
let (remaining, _peek_not_whitespace) =
|
||||||
peek(verify(anychar, |c| !c.is_whitespace() && *c != '\u{200B}'))(remaining)?;
|
peek(verify(anychar, |c| !c.is_whitespace() && *c != '\u{200B}'))(remaining)?;
|
||||||
let text_markup_end_specialized = text_markup_end(open.into());
|
let text_markup_end_specialized = text_markup_end(open.into(), remaining.get_byte_offset());
|
||||||
let contexts = [
|
let contexts = [
|
||||||
ContextElement::ContextObject(marker_symbol),
|
ContextElement::ContextObject(marker_symbol),
|
||||||
ContextElement::ExitMatcherNode(ExitMatcherNode {
|
ContextElement::ExitMatcherNode(ExitMatcherNode {
|
||||||
@@ -250,7 +244,7 @@ fn _text_markup_string<'b, 'g, 'r, 's, 'c>(
|
|||||||
let (remaining, open) = tag(marker_symbol)(remaining)?;
|
let (remaining, open) = tag(marker_symbol)(remaining)?;
|
||||||
let (remaining, _peek_not_whitespace) =
|
let (remaining, _peek_not_whitespace) =
|
||||||
peek(verify(anychar, |c| !c.is_whitespace() && *c != '\u{200B}'))(remaining)?;
|
peek(verify(anychar, |c| !c.is_whitespace() && *c != '\u{200B}'))(remaining)?;
|
||||||
let text_markup_end_specialized = text_markup_end(open.into());
|
let text_markup_end_specialized = text_markup_end(open.into(), remaining.get_byte_offset());
|
||||||
let contexts = [
|
let contexts = [
|
||||||
ContextElement::ContextObject(marker_symbol),
|
ContextElement::ContextObject(marker_symbol),
|
||||||
ContextElement::ExitMatcherNode(ExitMatcherNode {
|
ContextElement::ExitMatcherNode(ExitMatcherNode {
|
||||||
@@ -292,16 +286,22 @@ fn pre<'b, 'g, 'r, 's>(
|
|||||||
_context: RefContext<'b, 'g, 'r, 's>,
|
_context: RefContext<'b, 'g, 'r, 's>,
|
||||||
input: OrgSource<'s>,
|
input: OrgSource<'s>,
|
||||||
) -> Res<OrgSource<'s>, ()> {
|
) -> Res<OrgSource<'s>, ()> {
|
||||||
|
if start_of_line(input).is_ok() {
|
||||||
|
return Ok((input, ()));
|
||||||
|
}
|
||||||
|
if preceded_by_whitespace(true)(input).is_ok() {
|
||||||
|
return Ok((input, ()));
|
||||||
|
}
|
||||||
let preceding_character = input.get_preceding_character();
|
let preceding_character = input.get_preceding_character();
|
||||||
match preceding_character {
|
match preceding_character {
|
||||||
// If None, we are at the start of the file which is technically the beginning of a line.
|
// If None, we are at the start of the file which is technically the beginning of a line.
|
||||||
None | Some('\r') | Some('\n') | Some(' ') | Some('\t') | Some('-') | Some('(')
|
Some('-') | Some('(') | Some('{') | Some('\'') | Some('"') => {}
|
||||||
| Some('{') | Some('\'') | Some('"') | Some('<') => {}
|
|
||||||
Some(_) => {
|
Some(_) => {
|
||||||
return Err(nom::Err::Error(CustomError::MyError(MyError(
|
return Err(nom::Err::Error(CustomError::MyError(MyError(
|
||||||
"Not a valid pre character for text markup.".into(),
|
"Not a valid pre character for text markup.".into(),
|
||||||
))));
|
))));
|
||||||
}
|
}
|
||||||
|
None => unreachable!(), // None is for start of file, which should already be handled by the start_of_line matcher above.
|
||||||
};
|
};
|
||||||
Ok((input, ()))
|
Ok((input, ()))
|
||||||
}
|
}
|
||||||
@@ -311,12 +311,17 @@ fn post<'b, 'g, 'r, 's>(
|
|||||||
_context: RefContext<'b, 'g, 'r, 's>,
|
_context: RefContext<'b, 'g, 'r, 's>,
|
||||||
input: OrgSource<'s>,
|
input: OrgSource<'s>,
|
||||||
) -> Res<OrgSource<'s>, ()> {
|
) -> Res<OrgSource<'s>, ()> {
|
||||||
let (remaining, _) = alt((recognize(one_of(" \r\n\t-.,;:!?')}[\"")), line_ending))(input)?;
|
let (remaining, _) = alt((recognize(one_of(" \r\n\t-.,;:!?')}[\"\\")), line_ending))(input)?;
|
||||||
Ok((remaining, ()))
|
Ok((remaining, ()))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn text_markup_end<'c>(marker_symbol: &'c str) -> impl ContextMatcher + 'c {
|
fn text_markup_end<'c>(
|
||||||
move |context, input: OrgSource<'_>| _text_markup_end(context, input, marker_symbol)
|
marker_symbol: &'c str,
|
||||||
|
contents_start_offset: usize,
|
||||||
|
) -> impl ContextMatcher + 'c {
|
||||||
|
move |context, input: OrgSource<'_>| {
|
||||||
|
_text_markup_end(context, input, marker_symbol, contents_start_offset)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
|
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
|
||||||
@@ -324,7 +329,13 @@ fn _text_markup_end<'b, 'g, 'r, 's, 'c>(
|
|||||||
context: RefContext<'b, 'g, 'r, 's>,
|
context: RefContext<'b, 'g, 'r, 's>,
|
||||||
input: OrgSource<'s>,
|
input: OrgSource<'s>,
|
||||||
marker_symbol: &'c str,
|
marker_symbol: &'c str,
|
||||||
|
contents_start_offset: usize,
|
||||||
) -> Res<OrgSource<'s>, OrgSource<'s>> {
|
) -> Res<OrgSource<'s>, OrgSource<'s>> {
|
||||||
|
if input.get_byte_offset() == contents_start_offset {
|
||||||
|
return Err(nom::Err::Error(CustomError::MyError(MyError(
|
||||||
|
"Text markup cannot be empty".into(),
|
||||||
|
))));
|
||||||
|
}
|
||||||
not(preceded_by_whitespace(false))(input)?;
|
not(preceded_by_whitespace(false))(input)?;
|
||||||
let (remaining, _marker) = terminated(
|
let (remaining, _marker) = terminated(
|
||||||
tag(marker_symbol),
|
tag(marker_symbol),
|
||||||
@@ -354,6 +365,66 @@ impl<'x> RematchObject<'x> for Bold<'x> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<'x> RematchObject<'x> for Italic<'x> {
|
||||||
|
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
|
||||||
|
fn rematch_object<'b, 'g, 'r, 's>(
|
||||||
|
&'x self,
|
||||||
|
_context: RefContext<'b, 'g, 'r, 's>,
|
||||||
|
input: OrgSource<'s>,
|
||||||
|
) -> Res<OrgSource<'s>, Object<'s>> {
|
||||||
|
let (remaining, children) =
|
||||||
|
_rematch_text_markup_object(_context, input, "/", &self.children)?;
|
||||||
|
let source = get_consumed(input, remaining);
|
||||||
|
Ok((
|
||||||
|
remaining,
|
||||||
|
Object::Italic(Italic {
|
||||||
|
source: source.into(),
|
||||||
|
children,
|
||||||
|
}),
|
||||||
|
))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'x> RematchObject<'x> for Underline<'x> {
|
||||||
|
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
|
||||||
|
fn rematch_object<'b, 'g, 'r, 's>(
|
||||||
|
&'x self,
|
||||||
|
_context: RefContext<'b, 'g, 'r, 's>,
|
||||||
|
input: OrgSource<'s>,
|
||||||
|
) -> Res<OrgSource<'s>, Object<'s>> {
|
||||||
|
let (remaining, children) =
|
||||||
|
_rematch_text_markup_object(_context, input, "_", &self.children)?;
|
||||||
|
let source = get_consumed(input, remaining);
|
||||||
|
Ok((
|
||||||
|
remaining,
|
||||||
|
Object::Underline(Underline {
|
||||||
|
source: source.into(),
|
||||||
|
children,
|
||||||
|
}),
|
||||||
|
))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'x> RematchObject<'x> for StrikeThrough<'x> {
|
||||||
|
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
|
||||||
|
fn rematch_object<'b, 'g, 'r, 's>(
|
||||||
|
&'x self,
|
||||||
|
_context: RefContext<'b, 'g, 'r, 's>,
|
||||||
|
input: OrgSource<'s>,
|
||||||
|
) -> Res<OrgSource<'s>, Object<'s>> {
|
||||||
|
let (remaining, children) =
|
||||||
|
_rematch_text_markup_object(_context, input, "+", &self.children)?;
|
||||||
|
let source = get_consumed(input, remaining);
|
||||||
|
Ok((
|
||||||
|
remaining,
|
||||||
|
Object::StrikeThrough(StrikeThrough {
|
||||||
|
source: source.into(),
|
||||||
|
children,
|
||||||
|
}),
|
||||||
|
))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
|
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
|
||||||
fn _rematch_text_markup_object<'b, 'g, 'r, 's, 'x>(
|
fn _rematch_text_markup_object<'b, 'g, 'r, 's, 'x>(
|
||||||
context: RefContext<'b, 'g, 'r, 's>,
|
context: RefContext<'b, 'g, 'r, 's>,
|
||||||
@@ -364,7 +435,7 @@ fn _rematch_text_markup_object<'b, 'g, 'r, 's, 'x>(
|
|||||||
let (remaining, _) = pre(context, input)?;
|
let (remaining, _) = pre(context, input)?;
|
||||||
let (remaining, open) = tag(marker_symbol)(remaining)?;
|
let (remaining, open) = tag(marker_symbol)(remaining)?;
|
||||||
let (remaining, _peek_not_whitespace) = peek(not(multispace1))(remaining)?;
|
let (remaining, _peek_not_whitespace) = peek(not(multispace1))(remaining)?;
|
||||||
let text_markup_end_specialized = text_markup_end(open.into());
|
let text_markup_end_specialized = text_markup_end(open.into(), remaining.get_byte_offset());
|
||||||
let parser_context = ContextElement::ExitMatcherNode(ExitMatcherNode {
|
let parser_context = ContextElement::ExitMatcherNode(ExitMatcherNode {
|
||||||
class: ExitClass::Gamma,
|
class: ExitClass::Gamma,
|
||||||
exit_matcher: &text_markup_end_specialized,
|
exit_matcher: &text_markup_end_specialized,
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
use nom::branch::alt;
|
use nom::branch::alt;
|
||||||
|
use nom::bytes::complete::is_a;
|
||||||
use nom::character::complete::anychar;
|
use nom::character::complete::anychar;
|
||||||
use nom::character::complete::line_ending;
|
use nom::character::complete::line_ending;
|
||||||
use nom::character::complete::none_of;
|
use nom::character::complete::none_of;
|
||||||
@@ -228,9 +229,16 @@ where
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Match at least one space character.
|
||||||
|
///
|
||||||
|
/// This is similar to nom's space1 parser except space1 matches both spaces and tabs whereas this only matches spaces.
|
||||||
|
pub(crate) fn only_space1<'s>(input: OrgSource<'s>) -> Res<OrgSource<'s>, OrgSource<'s>> {
|
||||||
|
is_a(" ")(input)
|
||||||
|
}
|
||||||
|
|
||||||
/// Match single space or tab.
|
/// Match single space or tab.
|
||||||
///
|
///
|
||||||
/// In org-mode syntax, spaces and tabs are interchangeable.
|
/// In org-mode syntax, spaces and tabs are often (but not always!) interchangeable.
|
||||||
pub(crate) fn org_space<'s>(input: OrgSource<'s>) -> Res<OrgSource<'s>, char> {
|
pub(crate) fn org_space<'s>(input: OrgSource<'s>) -> Res<OrgSource<'s>, char> {
|
||||||
one_of(" \t")(input)
|
one_of(" \t")(input)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ use super::Object;
|
|||||||
use super::Source;
|
use super::Source;
|
||||||
|
|
||||||
pub type PriorityCookie = u8;
|
pub type PriorityCookie = u8;
|
||||||
|
pub type HeadlineLevel = u16;
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct Document<'s> {
|
pub struct Document<'s> {
|
||||||
@@ -14,7 +15,7 @@ pub struct Document<'s> {
|
|||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct Heading<'s> {
|
pub struct Heading<'s> {
|
||||||
pub source: &'s str,
|
pub source: &'s str,
|
||||||
pub stars: usize,
|
pub level: HeadlineLevel,
|
||||||
pub todo_keyword: Option<(TodoKeywordType, &'s str)>,
|
pub todo_keyword: Option<(TodoKeywordType, &'s str)>,
|
||||||
pub priority_cookie: Option<PriorityCookie>,
|
pub priority_cookie: Option<PriorityCookie>,
|
||||||
pub title: Vec<Object<'s>>,
|
pub title: Vec<Object<'s>>,
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ mod source;
|
|||||||
pub use document::Document;
|
pub use document::Document;
|
||||||
pub use document::DocumentElement;
|
pub use document::DocumentElement;
|
||||||
pub use document::Heading;
|
pub use document::Heading;
|
||||||
|
pub use document::HeadlineLevel;
|
||||||
pub use document::PriorityCookie;
|
pub use document::PriorityCookie;
|
||||||
pub use document::Section;
|
pub use document::Section;
|
||||||
pub use document::TodoKeywordType;
|
pub use document::TodoKeywordType;
|
||||||
|
|||||||
Reference in New Issue
Block a user