Compare commits
108 Commits
v0.1.9
...
5694f80f41
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5694f80f41 | ||
|
|
36327e92d7 | ||
|
|
c70eb69ed6 | ||
|
|
8d9ff77799 | ||
|
|
029791e0b2 | ||
|
|
8d621b32dc | ||
|
|
dfad7b7888 | ||
|
|
56348a6d54 | ||
|
|
65e142a215 | ||
|
|
aa0a0b890e | ||
|
|
ab33730830 | ||
|
|
4c8828b91b | ||
|
|
2ba5156ee1 | ||
|
|
ae3a6ff919 | ||
|
|
4716e1ce5b | ||
|
|
89fcf6cb54 | ||
|
|
f220fd63e5 | ||
|
|
7cf1f7b7bb | ||
|
|
135ca133ea | ||
|
|
d126488891 | ||
|
|
51748afd41 | ||
|
|
e84135985e | ||
|
|
448902bb05 | ||
|
|
65df18171a | ||
|
|
e2bc58a469 | ||
|
|
fbe3c76ab7 | ||
|
|
368c6a457e | ||
|
|
c7dbe596b3 | ||
|
|
3da52a0826 | ||
|
|
ae11e390d1 | ||
|
|
45dd38ac2d | ||
|
|
7af18e2312 | ||
|
|
1b603f3a05 | ||
|
|
d06e4de7b0 | ||
|
|
e686666ea0 | ||
|
|
672848d06a | ||
|
|
627c785e24 | ||
|
|
758e224e6d | ||
|
|
f79606047e | ||
|
|
dd3de67a8c | ||
|
|
823426a4f1 | ||
|
|
fa97124186 | ||
|
|
885fefd060 | ||
|
|
efac73798f | ||
|
|
68e392811e | ||
|
|
343af41f78 | ||
|
|
f49a1853ad | ||
|
|
6bd8d9efd7 | ||
|
|
18ad80e018 | ||
|
|
34a0858473 | ||
|
|
4ba9d7439a | ||
|
|
6f0439bb6d | ||
|
|
b478b6f5d7 | ||
|
|
02af3d0081 | ||
|
|
40685f05cc | ||
|
|
e21701b97c | ||
|
|
ef8a6884fe | ||
|
|
ac7125d9b6 | ||
|
|
58ca9569a6 | ||
|
|
1da521b08a | ||
|
|
386ad5091d | ||
|
|
5f84cd974d | ||
|
|
d8ea450a46 | ||
|
|
3742f4fa08 | ||
|
|
11a7234900 | ||
|
|
50a3631b79 | ||
|
|
da2d7535e8 | ||
|
|
1351577c5a | ||
|
|
65eda08843 | ||
|
|
b82d4c0eca | ||
|
|
93fe46e4e7 | ||
|
|
5b308ea76f | ||
|
|
ab4a0c1224 | ||
|
|
786521ad4a | ||
|
|
d8102b7bc2 | ||
|
|
a26640355c | ||
|
|
057c8a1387 | ||
|
|
4fc81e983a | ||
|
|
258e9485de | ||
|
|
87ac18e6b2 | ||
|
|
e1e4ac75e4 | ||
|
|
c877116540 | ||
|
|
8e70773b15 | ||
|
|
f046b16c11 | ||
|
|
1ab7d2f2d7 | ||
|
|
a548c7e170 | ||
|
|
b556f4617f | ||
|
|
13163f2468 | ||
|
|
da5dcd4c1b | ||
|
|
d059afef07 | ||
|
|
bcade66e68 | ||
|
|
301a6db83e | ||
|
|
32da06776c | ||
|
|
169bf69f5e | ||
|
|
7ee48ff65c | ||
|
|
afb43ff34f | ||
|
|
b56d847cfa | ||
|
|
1503054994 | ||
|
|
03028889bd | ||
|
|
317293f0f2 | ||
|
|
3d7f411cf9 | ||
|
|
650cbc17db | ||
|
|
1d7770e590 | ||
|
|
bf038db31c | ||
|
|
4cdf88a632 | ||
|
|
2eaef82fdb | ||
|
|
00dc7b636c | ||
|
|
e6c809ab03 |
7
Makefile
7
Makefile
@@ -46,6 +46,13 @@ dockertest:
|
|||||||
> $(MAKE) -C docker/organic_test
|
> $(MAKE) -C docker/organic_test
|
||||||
> docker run --init --rm -i -t --read-only -v "$$(readlink -f ./):/source:ro" --mount type=tmpfs,destination=/tmp --mount source=cargo-cache,target=/usr/local/cargo/registry --mount source=rust-cache,target=/target --env CARGO_TARGET_DIR=/target -w /source organic-test --no-default-features --features compare --no-fail-fast --lib --test test_loader -- --test-threads $(TESTJOBS)
|
> docker run --init --rm -i -t --read-only -v "$$(readlink -f ./):/source:ro" --mount type=tmpfs,destination=/tmp --mount source=cargo-cache,target=/usr/local/cargo/registry --mount source=rust-cache,target=/target --env CARGO_TARGET_DIR=/target -w /source organic-test --no-default-features --features compare --no-fail-fast --lib --test test_loader -- --test-threads $(TESTJOBS)
|
||||||
|
|
||||||
|
.PHONY: buildtest
|
||||||
|
buildtest:
|
||||||
|
> cargo build --no-default-features
|
||||||
|
> cargo build --no-default-features --features compare
|
||||||
|
> cargo build --no-default-features --features tracing
|
||||||
|
> cargo build --no-default-features --features compare,tracing
|
||||||
|
|
||||||
.PHONY: foreign_document_test
|
.PHONY: foreign_document_test
|
||||||
foreign_document_test:
|
foreign_document_test:
|
||||||
> $(MAKE) -C docker/organic_test run_foreign_document_test
|
> $(MAKE) -C docker/organic_test run_foreign_document_test
|
||||||
|
|||||||
17
org_mode_samples/affiliated_keyword/all_name.org
Normal file
17
org_mode_samples/affiliated_keyword/all_name.org
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
#+name: foo
|
||||||
|
: bar
|
||||||
|
|
||||||
|
#+source: foo
|
||||||
|
: bar
|
||||||
|
|
||||||
|
#+tblname: foo
|
||||||
|
: bar
|
||||||
|
|
||||||
|
#+resname: foo
|
||||||
|
: bar
|
||||||
|
|
||||||
|
#+srcname: foo
|
||||||
|
: bar
|
||||||
|
|
||||||
|
#+label: foo
|
||||||
|
: bar
|
||||||
8
org_mode_samples/affiliated_keyword/case_insensitive.org
Normal file
8
org_mode_samples/affiliated_keyword/case_insensitive.org
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
#+NAME: foo
|
||||||
|
bar
|
||||||
|
|
||||||
|
#+NaMe: baz
|
||||||
|
cat
|
||||||
|
|
||||||
|
#+name: lorem
|
||||||
|
ipsum
|
||||||
6
org_mode_samples/affiliated_keyword/multiple_name.org
Normal file
6
org_mode_samples/affiliated_keyword/multiple_name.org
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
#+name: foo
|
||||||
|
#+source: bar
|
||||||
|
#+name: baz
|
||||||
|
#+tblname: lorem
|
||||||
|
#+label: ipsum
|
||||||
|
: dolar
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
#+tblname: foo
|
||||||
|
: bar
|
||||||
10
org_mode_samples/greater_element/drawer/name.org
Normal file
10
org_mode_samples/greater_element/drawer/name.org
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
* Headline
|
||||||
|
before
|
||||||
|
#+NAME: foo
|
||||||
|
:candle:
|
||||||
|
inside
|
||||||
|
|
||||||
|
the drawer
|
||||||
|
:end:
|
||||||
|
|
||||||
|
after
|
||||||
7
org_mode_samples/greater_element/dynamic_block/name.org
Normal file
7
org_mode_samples/greater_element/dynamic_block/name.org
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
#+NAME: foo
|
||||||
|
#+BEGIN: clocktable :scope file :maxlevel 2
|
||||||
|
#+CAPTION: Clock summary at [2023-08-25 Fri 05:34]
|
||||||
|
| Headline | Time |
|
||||||
|
|--------------+--------|
|
||||||
|
| *Total time* | *0:00* |
|
||||||
|
#+END:
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
#+begin_center
|
||||||
|
|
||||||
|
#+end_center
|
||||||
|
|
||||||
|
#+begin_center
|
||||||
|
#+NAME: foo
|
||||||
|
|
||||||
|
#+end_center
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
#+NAME: foo
|
||||||
|
#+begin_center
|
||||||
|
|
||||||
|
#+end_center
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
#+NAME: foo
|
||||||
|
#+begin_quote
|
||||||
|
|
||||||
|
#+end_quote
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
#+NAME: foo
|
||||||
|
#+begin_defun
|
||||||
|
foo
|
||||||
|
|
||||||
|
{{{bar(baz)}}}
|
||||||
|
#+end_defun
|
||||||
2
org_mode_samples/greater_element/plain_list/name.org
Normal file
2
org_mode_samples/greater_element/plain_list/name.org
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
#+NAME: foo
|
||||||
|
1. bar
|
||||||
2
org_mode_samples/greater_element/table/name.org
Normal file
2
org_mode_samples/greater_element/table/name.org
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
#+NAME: foo
|
||||||
|
| foo | bar |
|
||||||
3
org_mode_samples/lesser_element/babel_call/empty.org
Normal file
3
org_mode_samples/lesser_element/babel_call/empty.org
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
#+call:
|
||||||
|
|
||||||
|
#+call:
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
#+call: foo[inside](bar="baz")[outside]
|
||||||
|
|
||||||
|
#+call: foo[](bar="baz")[]
|
||||||
@@ -1 +1,2 @@
|
|||||||
|
#+NAME: foo
|
||||||
#+call: foo(bar="baz")
|
#+call: foo(bar="baz")
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
#+call: foo[inside](bar="baz")[outside]
|
||||||
|
|
||||||
|
#+call: foo[[inside]](bar="baz")[outside]
|
||||||
|
|
||||||
|
#+call: foo[inside]((bar="baz"))[outside]
|
||||||
|
|
||||||
|
#+call: foo[inside](bar="baz")[[outside]]
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
#+call: foo(bar="baz"
|
||||||
7
org_mode_samples/lesser_element/babel_call/simple.org
Normal file
7
org_mode_samples/lesser_element/babel_call/simple.org
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
#+call: foo(bar="baz")
|
||||||
|
|
||||||
|
#+call: lorem ipsum
|
||||||
|
|
||||||
|
#+call: dolar cat(dog)
|
||||||
|
|
||||||
|
#+call: (bat)
|
||||||
3
org_mode_samples/lesser_element/babel_call/spaces.org
Normal file
3
org_mode_samples/lesser_element/babel_call/spaces.org
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
#+call: foo [inside] (bar="baz") [outside]
|
||||||
|
|
||||||
|
#+call: foo (bar="baz") [outside]
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
CLOCK: [2023-04-21 Fri 19:32]--[2023-04-21 Fri 19:35]
|
||||||
3
org_mode_samples/lesser_element/clock/name.org
Normal file
3
org_mode_samples/lesser_element/clock/name.org
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
CLOCK: [2023-04-21 Fri 19:32]--[2023-04-21 Fri 19:35] => 0:03
|
||||||
|
#+NAME: foo
|
||||||
|
CLOCK: [2023-04-21 Fri 19:43]
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
CLOCK: [2023-04-21 Fri 19:43] => 0:03
|
||||||
2
org_mode_samples/lesser_element/clock/time_range.org
Normal file
2
org_mode_samples/lesser_element/clock/time_range.org
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
CLOCK: [1970-01-01 Thu 8:15-13:15otherrest +1w -1d] => 0:03
|
||||||
|
CLOCK: [1970-01-01 Thu 8:15-13:15otherrest +1w -1d]
|
||||||
2
org_mode_samples/lesser_element/comment/name.org
Normal file
2
org_mode_samples/lesser_element/comment/name.org
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
#+NAME: foo
|
||||||
|
# Comments cannot have affiliated keywords.
|
||||||
2
org_mode_samples/lesser_element/diary_sexp/name.org
Normal file
2
org_mode_samples/lesser_element/diary_sexp/name.org
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
#+NAME: foo
|
||||||
|
%%(foo)
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
#+NAME: foo
|
||||||
|
: bar
|
||||||
2
org_mode_samples/lesser_element/horizontal_rule/name.org
Normal file
2
org_mode_samples/lesser_element/horizontal_rule/name.org
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
#+NAME: foo
|
||||||
|
-----
|
||||||
2
org_mode_samples/lesser_element/keyword/name.org
Normal file
2
org_mode_samples/lesser_element/keyword/name.org
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
#+NAME: foo
|
||||||
|
#+FOO: BAR
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
#+NAME: foo
|
||||||
|
\begin{foo}
|
||||||
|
bar
|
||||||
|
\end{foo}
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
#+begin_comment
|
||||||
|
,* foo
|
||||||
|
,,,** bar
|
||||||
|
,*** baz
|
||||||
|
lorem
|
||||||
|
, ipsum
|
||||||
|
,#+begin_src dolar
|
||||||
|
|
||||||
|
,#+end_src
|
||||||
|
#+end_comment
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
#+NAME: foo
|
||||||
|
#+begin_comment text
|
||||||
|
bar
|
||||||
|
#+end_comment
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
#+begin_comment
|
||||||
|
This is a comment
|
||||||
|
,* with an escaped line.
|
||||||
|
#+end_comment
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
#+begin_comment -n 20
|
||||||
|
foo
|
||||||
|
#+end_comment
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
#+begin_example
|
||||||
|
,* foo
|
||||||
|
,,,** bar
|
||||||
|
,*** baz
|
||||||
|
lorem
|
||||||
|
, ipsum
|
||||||
|
,#+begin_src dolar
|
||||||
|
|
||||||
|
,#+end_src
|
||||||
|
#+end_example
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
#+begin_example python :exports results
|
||||||
|
print("foo")
|
||||||
|
#+end_example
|
||||||
|
|
||||||
|
#+begin_example python -n :exports results
|
||||||
|
print("foo")
|
||||||
|
#+end_example
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
#+begin_example elisp -n 5
|
||||||
|
foo
|
||||||
|
#+end_example
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
#+begin_example -n -10
|
||||||
|
foo
|
||||||
|
#+end_example
|
||||||
|
|
||||||
|
#+begin_example +n -15
|
||||||
|
bar
|
||||||
|
#+end_example
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
#+begin_example -n 0
|
||||||
|
foo
|
||||||
|
#+end_example
|
||||||
|
|
||||||
|
#+begin_example +n 0
|
||||||
|
bar
|
||||||
|
#+end_example
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
#+begin_example -n 5
|
||||||
|
foo
|
||||||
|
#+end_example
|
||||||
|
|
||||||
|
# Line numbering starts at 15 for the example below since it uses +n.
|
||||||
|
#+begin_example +n 10
|
||||||
|
bar
|
||||||
|
#+end_example
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
#+NAME: foo
|
||||||
|
#+begin_example text
|
||||||
|
bar
|
||||||
|
#+end_example
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
#+begin_example foo -k
|
||||||
|
bar
|
||||||
|
#+end_example
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
#+begin_example foo -n bar -k baz
|
||||||
|
|
||||||
|
#+end_example
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
#+begin_example text -i
|
||||||
|
foo
|
||||||
|
#+end_example
|
||||||
|
|
||||||
|
#+begin_example text -n -i
|
||||||
|
foo
|
||||||
|
#+end_example
|
||||||
|
|
||||||
|
#+begin_example text
|
||||||
|
foo
|
||||||
|
#+end_example
|
||||||
|
|
||||||
|
#+begin_example text -n -r -k
|
||||||
|
foo
|
||||||
|
#+end_example
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
#+begin_example text
|
||||||
|
foo
|
||||||
|
bar (ref:here)
|
||||||
|
baz
|
||||||
|
#+end_example
|
||||||
|
Link to the reference: [[(here)]]
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
#+begin_example
|
||||||
|
#+end_example
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
#+BEGIN_EXAMPLE elisp -n -r -l "((%s))"
|
||||||
|
foo
|
||||||
|
#+END_EXAMPLE
|
||||||
|
|
||||||
|
#+BEGIN_EXAMPLE elisp -k -n -r -l "((%s))"
|
||||||
|
foo
|
||||||
|
#+END_EXAMPLE
|
||||||
|
|
||||||
|
#+BEGIN_EXAMPLE elisp -k 8 -n -r -l "((%s))"
|
||||||
|
foo
|
||||||
|
#+END_EXAMPLE
|
||||||
|
|
||||||
|
#+BEGIN_EXAMPLE elisp -n -r -k -l "((%s))"
|
||||||
|
foo
|
||||||
|
#+END_EXAMPLE
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
#+begin_example +n 10
|
||||||
|
foo
|
||||||
|
#+end_example
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
#+begin_export html
|
||||||
|
,* foo
|
||||||
|
,,,** bar
|
||||||
|
,*** baz
|
||||||
|
lorem
|
||||||
|
, ipsum
|
||||||
|
,#+begin_src dolar
|
||||||
|
|
||||||
|
,#+end_src
|
||||||
|
#+end_export
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
#+NAME: foo
|
||||||
|
#+begin_export text
|
||||||
|
bar
|
||||||
|
#+end_export
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
#+begin_export latex
|
||||||
|
This would be LaTeX code.
|
||||||
|
#+end_export
|
||||||
6
org_mode_samples/lesser_element/lesser_block/nested.org
Normal file
6
org_mode_samples/lesser_element/lesser_block/nested.org
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
# Verse blocks are the only lesser blocks that contain objects
|
||||||
|
#+begin_verse
|
||||||
|
#+begin_comment
|
||||||
|
This is a comment.
|
||||||
|
#+end_comment
|
||||||
|
#+end_verse
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
#+begin_src python :exports results
|
||||||
|
print("foo")
|
||||||
|
#+end_src
|
||||||
|
|
||||||
|
#+begin_src python -n :exports results
|
||||||
|
print("foo")
|
||||||
|
#+end_src
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
#+NAME: foo
|
||||||
|
#+begin_src text
|
||||||
|
bar
|
||||||
|
#+end_src
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
#+begin_src python :exports results
|
||||||
|
print("foo")
|
||||||
|
#+end_src
|
||||||
|
|
||||||
|
#+begin_src python -n :exports results
|
||||||
|
print("foo")
|
||||||
|
#+end_src
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
#+begin_src foo -n bar -k baz
|
||||||
|
|
||||||
|
#+end_src
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
#+begin_src text -i
|
||||||
|
foo
|
||||||
|
#+end_src
|
||||||
|
|
||||||
|
#+begin_src text -n -i
|
||||||
|
foo
|
||||||
|
#+end_src
|
||||||
|
|
||||||
|
#+begin_src text
|
||||||
|
foo
|
||||||
|
#+end_src
|
||||||
|
|
||||||
|
#+begin_src text -n -r -k
|
||||||
|
foo
|
||||||
|
#+end_src
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
#+begin_src text
|
||||||
|
foo
|
||||||
|
bar (ref:here)
|
||||||
|
baz
|
||||||
|
#+end_src
|
||||||
|
Link to the reference: [[(here)]]
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
#+NAME: foo
|
||||||
|
#+begin_verse text
|
||||||
|
bar
|
||||||
|
#+end_verse
|
||||||
2
org_mode_samples/lesser_element/paragraph/name.org
Normal file
2
org_mode_samples/lesser_element/paragraph/name.org
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
#+NAME: foo
|
||||||
|
bar
|
||||||
13
org_mode_samples/object/regular_link/code_ref_link.org
Normal file
13
org_mode_samples/object/regular_link/code_ref_link.org
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
[[(foo)]]
|
||||||
|
|
||||||
|
[[((bar))]]
|
||||||
|
|
||||||
|
[[((baz)]]
|
||||||
|
|
||||||
|
[[(lo
|
||||||
|
rem)]]
|
||||||
|
|
||||||
|
# These become fuzzy
|
||||||
|
[[(foo) ]]
|
||||||
|
[[ (foo)]]
|
||||||
|
[[(foo)::3]]
|
||||||
6
org_mode_samples/object/regular_link/custom_id_link.org
Normal file
6
org_mode_samples/object/regular_link/custom_id_link.org
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
[[#foo]]
|
||||||
|
|
||||||
|
[[#fo
|
||||||
|
o]]
|
||||||
|
|
||||||
|
[[#foo::3]]
|
||||||
21
org_mode_samples/object/regular_link/file_link.org
Normal file
21
org_mode_samples/object/regular_link/file_link.org
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
[[./simple.org]]
|
||||||
|
[[../simple.org]]
|
||||||
|
[[/simple.org]]
|
||||||
|
[[file:simple.org]]
|
||||||
|
[[file:sim ple.org]]
|
||||||
|
|
||||||
|
[[file:simp
|
||||||
|
le.org]]
|
||||||
|
|
||||||
|
[[file:simple.org::3]]
|
||||||
|
[[file:simple.org::foo]]
|
||||||
|
[[file:simple.org::#foo]]
|
||||||
|
[[file:simple.org::foo bar]]
|
||||||
|
[[file:simple.org::foo
|
||||||
|
bar]]
|
||||||
|
[[file:simple.org::foo
|
||||||
|
bar]]
|
||||||
|
[[file:simple.org::foo
|
||||||
|
bar]]
|
||||||
|
[[file:simple.org::foo::bar]]
|
||||||
|
[[file:simple.org::/foo/]]
|
||||||
6
org_mode_samples/object/regular_link/fuzzy_link.org
Normal file
6
org_mode_samples/object/regular_link/fuzzy_link.org
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
[[elisp.org]]
|
||||||
|
|
||||||
|
[[eli
|
||||||
|
sp.org]]
|
||||||
|
|
||||||
|
[[elisp.org::3]]
|
||||||
6
org_mode_samples/object/regular_link/id_link.org
Normal file
6
org_mode_samples/object/regular_link/id_link.org
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
[[id:83986bdf-987c-465d-8851-44cb4c02a86c]]
|
||||||
|
|
||||||
|
[[id:83986bdf-987c-465d
|
||||||
|
-8851-44cb4c02a86c]]
|
||||||
|
|
||||||
|
[[id:83986bdf-987c-465d-8851-44cb4c02a86c::foo]]
|
||||||
6
org_mode_samples/object/regular_link/protocol_link.org
Normal file
6
org_mode_samples/object/regular_link/protocol_link.org
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
[[shell:foo]]
|
||||||
|
|
||||||
|
[[shell:fo
|
||||||
|
o]]
|
||||||
|
|
||||||
|
[[shell:foo::3]]
|
||||||
7
org_mode_samples/object/regular_link/template.org
Normal file
7
org_mode_samples/object/regular_link/template.org
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
#+LINK: foo https://foo.bar/baz#%s
|
||||||
|
[[foo::lorem]]
|
||||||
|
[[foo::ipsum][dolar]]
|
||||||
|
|
||||||
|
[[cat::bat]]
|
||||||
|
#+LINK: cat dog%s
|
||||||
|
[[cat:bat]]
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
|
|
||||||
use crate::compare::diff::compare_document;
|
use crate::compare::diff::compare_document;
|
||||||
|
use crate::compare::diff::DiffResult;
|
||||||
use crate::compare::parse::emacs_parse_anonymous_org_document;
|
use crate::compare::parse::emacs_parse_anonymous_org_document;
|
||||||
use crate::compare::parse::emacs_parse_file_org_document;
|
use crate::compare::parse::emacs_parse_file_org_document;
|
||||||
use crate::compare::parse::get_emacs_version;
|
use crate::compare::parse::get_emacs_version;
|
||||||
@@ -43,6 +44,12 @@ pub fn run_anonymous_compare_with_settings<P: AsRef<str>>(
|
|||||||
|
|
||||||
if diff_result.is_bad() {
|
if diff_result.is_bad() {
|
||||||
Err("Diff results do not match.")?;
|
Err("Diff results do not match.")?;
|
||||||
|
} else {
|
||||||
|
println!(
|
||||||
|
"{color}Entire document passes.{reset}",
|
||||||
|
color = DiffResult::foreground_color(0, 255, 0),
|
||||||
|
reset = DiffResult::reset_color(),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
@@ -83,6 +90,12 @@ pub fn run_compare_on_file_with_settings<P: AsRef<Path>>(
|
|||||||
|
|
||||||
if diff_result.is_bad() {
|
if diff_result.is_bad() {
|
||||||
Err("Diff results do not match.")?;
|
Err("Diff results do not match.")?;
|
||||||
|
} else {
|
||||||
|
println!(
|
||||||
|
"{color}Entire document passes.{reset}",
|
||||||
|
color = DiffResult::foreground_color(0, 255, 0),
|
||||||
|
reset = DiffResult::reset_color(),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|||||||
104
src/compare/compare_field.rs
Normal file
104
src/compare/compare_field.rs
Normal file
@@ -0,0 +1,104 @@
|
|||||||
|
use std::fmt::Debug;
|
||||||
|
|
||||||
|
use super::diff::DiffStatus;
|
||||||
|
use super::sexp::Token;
|
||||||
|
use super::util::get_property;
|
||||||
|
use super::util::get_property_quoted_string;
|
||||||
|
use super::util::get_property_unquoted_atom;
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub(crate) enum EmacsField<'s> {
|
||||||
|
Required(&'s str),
|
||||||
|
#[allow(dead_code)]
|
||||||
|
Optional(&'s str),
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Do no comparison.
|
||||||
|
///
|
||||||
|
/// This is for when you want to acknowledge that a field exists in the emacs token, but you do not have any validation for it when using the compare_properties!() macro. Ideally, this should be kept to a minimum since this represents untested values.
|
||||||
|
#[allow(dead_code)]
|
||||||
|
pub(crate) fn compare_noop<'b, 's, 'x, R, RG>(
|
||||||
|
_emacs: &'b Token<'s>,
|
||||||
|
_rust_node: R,
|
||||||
|
_emacs_field: &'x str,
|
||||||
|
_rust_value_getter: RG,
|
||||||
|
) -> Result<Option<(DiffStatus, Option<String>)>, Box<dyn std::error::Error>> {
|
||||||
|
Ok(None)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Do no comparison.
|
||||||
|
///
|
||||||
|
/// This is for when you want to acknowledge that a field exists in the emacs token, but you do not have any validation for it when using the compare_properties!() macro. Ideally, this should be kept to a minimum since this represents untested values.
|
||||||
|
#[allow(dead_code)]
|
||||||
|
pub(crate) fn compare_identity() -> () {
|
||||||
|
()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Assert that the emacs value is always nil or absent.
|
||||||
|
///
|
||||||
|
/// This is usually used for fields which, in my testing, are always nil. Using this compare function instead of simply doing a compare_noop will enable us to be alerted when we finally come across an org-mode document that has a value other than nil for the property.
|
||||||
|
pub(crate) fn compare_property_always_nil<'b, 's, 'x, R, RG>(
|
||||||
|
emacs: &'b Token<'s>,
|
||||||
|
_rust_node: R,
|
||||||
|
emacs_field: &'x str,
|
||||||
|
_rust_value_getter: RG,
|
||||||
|
) -> Result<Option<(DiffStatus, Option<String>)>, Box<dyn std::error::Error>> {
|
||||||
|
let value = get_property(emacs, emacs_field)?;
|
||||||
|
if value.is_some() {
|
||||||
|
let this_status = DiffStatus::Bad;
|
||||||
|
let message = Some(format!(
|
||||||
|
"{} was expected to always be nil: {:?}",
|
||||||
|
emacs_field, value
|
||||||
|
));
|
||||||
|
Ok(Some((this_status, message)))
|
||||||
|
} else {
|
||||||
|
Ok(None)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn compare_property_quoted_string<
|
||||||
|
'b,
|
||||||
|
's,
|
||||||
|
'x,
|
||||||
|
R,
|
||||||
|
RV: AsRef<str> + std::fmt::Debug,
|
||||||
|
RG: Fn(R) -> Option<RV>,
|
||||||
|
>(
|
||||||
|
emacs: &'b Token<'s>,
|
||||||
|
rust_node: R,
|
||||||
|
emacs_field: &'x str,
|
||||||
|
rust_value_getter: RG,
|
||||||
|
) -> Result<Option<(DiffStatus, Option<String>)>, Box<dyn std::error::Error>> {
|
||||||
|
let value = get_property_quoted_string(emacs, emacs_field)?;
|
||||||
|
let rust_value = rust_value_getter(rust_node);
|
||||||
|
if rust_value.as_ref().map(|s| s.as_ref()) != value.as_ref().map(String::as_str) {
|
||||||
|
let this_status = DiffStatus::Bad;
|
||||||
|
let message = Some(format!(
|
||||||
|
"{} mismatch (emacs != rust) {:?} != {:?}",
|
||||||
|
emacs_field, value, rust_value
|
||||||
|
));
|
||||||
|
Ok(Some((this_status, message)))
|
||||||
|
} else {
|
||||||
|
Ok(None)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn compare_property_unquoted_atom<'b, 's, 'x, R, RG: Fn(R) -> Option<&'s str>>(
|
||||||
|
emacs: &'b Token<'s>,
|
||||||
|
rust_node: R,
|
||||||
|
emacs_field: &'x str,
|
||||||
|
rust_value_getter: RG,
|
||||||
|
) -> Result<Option<(DiffStatus, Option<String>)>, Box<dyn std::error::Error>> {
|
||||||
|
let value = get_property_unquoted_atom(emacs, emacs_field)?;
|
||||||
|
let rust_value = rust_value_getter(rust_node);
|
||||||
|
if rust_value != value {
|
||||||
|
let this_status = DiffStatus::Bad;
|
||||||
|
let message = Some(format!(
|
||||||
|
"{} mismatch (emacs != rust) {:?} != {:?}",
|
||||||
|
emacs_field, value, rust_value
|
||||||
|
));
|
||||||
|
Ok(Some((this_status, message)))
|
||||||
|
} else {
|
||||||
|
Ok(None)
|
||||||
|
}
|
||||||
|
}
|
||||||
1042
src/compare/diff.rs
1042
src/compare/diff.rs
File diff suppressed because it is too large
Load Diff
155
src/compare/macros.rs
Normal file
155
src/compare/macros.rs
Normal file
@@ -0,0 +1,155 @@
|
|||||||
|
/// Assert only the listed properties exist on the Emacs AST node and compare their values to the values on the rust AST node.
|
||||||
|
///
|
||||||
|
/// Example invocation:
|
||||||
|
/// ```
|
||||||
|
/// if let Some((new_status, new_message)) = compare_properties!(
|
||||||
|
/// emacs,
|
||||||
|
/// rust,
|
||||||
|
/// (
|
||||||
|
/// EmacsField::Required(":key"),
|
||||||
|
/// |r| Some(r.key),
|
||||||
|
/// compare_property_quoted_string
|
||||||
|
/// ),
|
||||||
|
/// (
|
||||||
|
/// EmacsField::Required(":value"),
|
||||||
|
/// |r| Some(r.contents),
|
||||||
|
/// compare_property_quoted_string
|
||||||
|
/// ),
|
||||||
|
/// (EmacsField::Required(":pre-blank"), compare_identity, compare_noop)
|
||||||
|
/// )? {
|
||||||
|
/// this_status = new_status;
|
||||||
|
/// message = new_message;
|
||||||
|
/// }
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
/// Or if the node has no properties aside from :standard-properties, we can still assert that the node has no unexpected properties:
|
||||||
|
/// ```
|
||||||
|
/// if let Some((new_status, new_message)) = compare_properties!(emacs)? {
|
||||||
|
/// this_status = new_status;
|
||||||
|
/// message = new_message;
|
||||||
|
/// }
|
||||||
|
/// ```
|
||||||
|
macro_rules! compare_properties {
|
||||||
|
($emacs:expr, $rust:expr, $(($emacs_field:expr, $rust_value_getter:expr, $compare_fn: expr)),+) => {
|
||||||
|
{
|
||||||
|
let mut this_status = DiffStatus::Good;
|
||||||
|
let mut message: Option<String> = None;
|
||||||
|
let children = $emacs.as_list()?;
|
||||||
|
let attributes_child = children
|
||||||
|
.iter()
|
||||||
|
.nth(1)
|
||||||
|
.ok_or("Should have an attributes child.")?;
|
||||||
|
let attributes_map = attributes_child.as_map()?;
|
||||||
|
let mut emacs_keys: BTreeSet<&str> = attributes_map.keys().map(|s| *s).collect();
|
||||||
|
if emacs_keys.contains(":standard-properties") {
|
||||||
|
emacs_keys.remove(":standard-properties");
|
||||||
|
} else {
|
||||||
|
this_status = DiffStatus::Bad;
|
||||||
|
message = Some(format!(
|
||||||
|
"Emacs token lacks :standard-properties field.",
|
||||||
|
));
|
||||||
|
}
|
||||||
|
$(
|
||||||
|
match $emacs_field {
|
||||||
|
EmacsField::Required(name) if emacs_keys.contains(name) => {
|
||||||
|
emacs_keys.remove(name);
|
||||||
|
},
|
||||||
|
EmacsField::Optional(name) if emacs_keys.contains(name) => {
|
||||||
|
emacs_keys.remove(name);
|
||||||
|
},
|
||||||
|
EmacsField::Required(name) => {
|
||||||
|
this_status = DiffStatus::Bad;
|
||||||
|
message = Some(format!(
|
||||||
|
"Emacs token lacks required field: {}",
|
||||||
|
name
|
||||||
|
));
|
||||||
|
},
|
||||||
|
EmacsField::Optional(_name) => {},
|
||||||
|
}
|
||||||
|
)+
|
||||||
|
|
||||||
|
if !emacs_keys.is_empty() {
|
||||||
|
let unexpected_keys: Vec<&str> = emacs_keys.into_iter().collect();
|
||||||
|
let unexpected_keys = unexpected_keys.join(", ");
|
||||||
|
this_status = DiffStatus::Bad;
|
||||||
|
message = Some(format!(
|
||||||
|
"Emacs token had extra field(s): {}",
|
||||||
|
unexpected_keys
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
$(
|
||||||
|
let emacs_name = match $emacs_field {
|
||||||
|
EmacsField::Required(name) => {
|
||||||
|
name
|
||||||
|
},
|
||||||
|
EmacsField::Optional(name) => {
|
||||||
|
name
|
||||||
|
},
|
||||||
|
};
|
||||||
|
let result = $compare_fn($emacs, $rust, emacs_name, $rust_value_getter)?;
|
||||||
|
match result {
|
||||||
|
Some((DiffStatus::Good, _)) => unreachable!("No comparison functions should return Some() when DiffStatus is good."),
|
||||||
|
Some((status, msg)) => {
|
||||||
|
this_status = status;
|
||||||
|
message = msg;
|
||||||
|
},
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
)+
|
||||||
|
|
||||||
|
match this_status {
|
||||||
|
DiffStatus::Good => {
|
||||||
|
let result: Result<_, Box<dyn std::error::Error>> = Ok(None);
|
||||||
|
result
|
||||||
|
},
|
||||||
|
_ => {
|
||||||
|
Ok(Some((this_status, message)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
// Default case for when there are no expected properties except for :standard-properties
|
||||||
|
($emacs:expr) => {
|
||||||
|
{
|
||||||
|
let mut this_status = DiffStatus::Good;
|
||||||
|
let mut message: Option<String> = None;
|
||||||
|
let children = $emacs.as_list()?;
|
||||||
|
let attributes_child = children
|
||||||
|
.iter()
|
||||||
|
.nth(1)
|
||||||
|
.ok_or("Should have an attributes child.")?;
|
||||||
|
let attributes_map = attributes_child.as_map()?;
|
||||||
|
let mut emacs_keys: BTreeSet<&str> = attributes_map.keys().map(|s| *s).collect();
|
||||||
|
if emacs_keys.contains(":standard-properties") {
|
||||||
|
emacs_keys.remove(":standard-properties");
|
||||||
|
} else {
|
||||||
|
this_status = DiffStatus::Bad;
|
||||||
|
message = Some(format!(
|
||||||
|
"Emacs token lacks :standard-properties field.",
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
if !emacs_keys.is_empty() {
|
||||||
|
let unexpected_keys: Vec<&str> = emacs_keys.into_iter().collect();
|
||||||
|
let unexpected_keys = unexpected_keys.join(", ");
|
||||||
|
this_status = DiffStatus::Bad;
|
||||||
|
message = Some(format!(
|
||||||
|
"Emacs token had extra field(s): {}",
|
||||||
|
unexpected_keys
|
||||||
|
));
|
||||||
|
}
|
||||||
|
match this_status {
|
||||||
|
DiffStatus::Good => {
|
||||||
|
let result: Result<_, Box<dyn std::error::Error>> = Ok(None);
|
||||||
|
result
|
||||||
|
},
|
||||||
|
_ => {
|
||||||
|
Ok(Some((this_status, message)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) use compare_properties;
|
||||||
@@ -1,6 +1,8 @@
|
|||||||
mod compare;
|
mod compare;
|
||||||
|
mod compare_field;
|
||||||
mod diff;
|
mod diff;
|
||||||
mod elisp_fact;
|
mod elisp_fact;
|
||||||
|
mod macros;
|
||||||
mod parse;
|
mod parse;
|
||||||
mod sexp;
|
mod sexp;
|
||||||
mod util;
|
mod util;
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ use crate::error::CustomError;
|
|||||||
use crate::error::MyError;
|
use crate::error::MyError;
|
||||||
use crate::error::Res;
|
use crate::error::Res;
|
||||||
use crate::parser::OrgSource;
|
use crate::parser::OrgSource;
|
||||||
|
use crate::types::Keyword;
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub(crate) enum ContextElement<'r, 's> {
|
pub(crate) enum ContextElement<'r, 's> {
|
||||||
@@ -27,11 +28,22 @@ pub(crate) enum ContextElement<'r, 's> {
|
|||||||
/// Indicates if elements should consume the whitespace after them.
|
/// Indicates if elements should consume the whitespace after them.
|
||||||
ConsumeTrailingWhitespace(bool),
|
ConsumeTrailingWhitespace(bool),
|
||||||
|
|
||||||
|
/// Indicate that we are parsing a paragraph that already has affiliated keywords.
|
||||||
|
///
|
||||||
|
/// The value stored is the start of the element after the affiliated keywords. In this way, we can ensure that we do not exit an element immediately after the affiliated keyword had been consumed.
|
||||||
|
HasAffiliatedKeyword(HasAffiliatedKeywordInner<'r, 's>),
|
||||||
|
|
||||||
/// This is just here to use the 's lifetime until I'm sure we can eliminate it from ContextElement.
|
/// This is just here to use the 's lifetime until I'm sure we can eliminate it from ContextElement.
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
Placeholder(PhantomData<&'s str>),
|
Placeholder(PhantomData<&'s str>),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub(crate) struct HasAffiliatedKeywordInner<'r, 's> {
|
||||||
|
pub(crate) start_after_affiliated_keywords: OrgSource<'s>,
|
||||||
|
pub(crate) keywords: &'r Vec<Keyword<'s>>,
|
||||||
|
}
|
||||||
|
|
||||||
pub(crate) struct ExitMatcherNode<'r> {
|
pub(crate) struct ExitMatcherNode<'r> {
|
||||||
// TODO: Should this be "&'r DynContextMatcher<'c>" ?
|
// TODO: Should this be "&'r DynContextMatcher<'c>" ?
|
||||||
pub(crate) exit_matcher: &'r DynContextMatcher<'r>,
|
pub(crate) exit_matcher: &'r DynContextMatcher<'r>,
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
use std::collections::BTreeMap;
|
||||||
use std::collections::BTreeSet;
|
use std::collections::BTreeSet;
|
||||||
|
|
||||||
use super::FileAccessInterface;
|
use super::FileAccessInterface;
|
||||||
@@ -32,6 +33,23 @@ pub struct GlobalSettings<'g, 's> {
|
|||||||
///
|
///
|
||||||
/// Corresponds to org-footnote-section elisp variable.
|
/// Corresponds to org-footnote-section elisp variable.
|
||||||
pub footnote_section: &'g str,
|
pub footnote_section: &'g str,
|
||||||
|
|
||||||
|
/// The label format for references inside src/example blocks.
|
||||||
|
///
|
||||||
|
/// Corresponds to org-coderef-label-format elisp variable.
|
||||||
|
pub coderef_label_format: &'g str,
|
||||||
|
|
||||||
|
/// The allowed protocols for links (for example, the "https" in "https://foo.bar/").
|
||||||
|
///
|
||||||
|
/// Corresponds to org-link-parameters elisp variable.
|
||||||
|
pub link_parameters: &'g [&'g str],
|
||||||
|
|
||||||
|
/// Link templates where the key is the document text and the value is the replacement.
|
||||||
|
///
|
||||||
|
/// For example, `"foo": "bar%s"` will replace `[[foo::baz]]` with `[[barbaz]]`
|
||||||
|
///
|
||||||
|
/// This is set by including #+LINK in the org-mode document.
|
||||||
|
pub link_templates: BTreeMap<String, String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub const DEFAULT_TAB_WIDTH: IndentationLevel = 8;
|
pub const DEFAULT_TAB_WIDTH: IndentationLevel = 8;
|
||||||
@@ -49,6 +67,9 @@ impl<'g, 's> GlobalSettings<'g, 's> {
|
|||||||
tab_width: DEFAULT_TAB_WIDTH,
|
tab_width: DEFAULT_TAB_WIDTH,
|
||||||
odd_levels_only: HeadlineLevelFilter::default(),
|
odd_levels_only: HeadlineLevelFilter::default(),
|
||||||
footnote_section: "Footnotes",
|
footnote_section: "Footnotes",
|
||||||
|
coderef_label_format: "(ref:%s)",
|
||||||
|
link_parameters: &DEFAULT_ORG_LINK_PARAMETERS,
|
||||||
|
link_templates: BTreeMap::new(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -70,3 +91,29 @@ impl Default for HeadlineLevelFilter {
|
|||||||
HeadlineLevelFilter::OddEven
|
HeadlineLevelFilter::OddEven
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const DEFAULT_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",
|
||||||
|
];
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ type DynMatcher<'c> = dyn Matcher + 'c;
|
|||||||
pub(crate) use context::Context;
|
pub(crate) use context::Context;
|
||||||
pub(crate) use context::ContextElement;
|
pub(crate) use context::ContextElement;
|
||||||
pub(crate) use context::ExitMatcherNode;
|
pub(crate) use context::ExitMatcherNode;
|
||||||
|
pub(crate) use context::HasAffiliatedKeywordInner;
|
||||||
pub(crate) use exiting::ExitClass;
|
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;
|
||||||
|
|||||||
240
src/parser/babel_call.rs
Normal file
240
src/parser/babel_call.rs
Normal file
@@ -0,0 +1,240 @@
|
|||||||
|
use nom::branch::alt;
|
||||||
|
use nom::bytes::complete::tag;
|
||||||
|
use nom::bytes::complete::tag_no_case;
|
||||||
|
use nom::character::complete::anychar;
|
||||||
|
use nom::character::complete::one_of;
|
||||||
|
use nom::character::complete::space0;
|
||||||
|
use nom::combinator::consumed;
|
||||||
|
use nom::combinator::opt;
|
||||||
|
use nom::combinator::peek;
|
||||||
|
use nom::combinator::recognize;
|
||||||
|
use nom::combinator::verify;
|
||||||
|
use nom::multi::many0;
|
||||||
|
use nom::multi::many_till;
|
||||||
|
use nom::sequence::tuple;
|
||||||
|
use nom::InputTake;
|
||||||
|
|
||||||
|
use super::keyword::affiliated_keyword;
|
||||||
|
use super::org_source::BracketDepth;
|
||||||
|
use super::util::get_name;
|
||||||
|
use super::util::maybe_consume_trailing_whitespace_if_not_exiting;
|
||||||
|
use super::util::start_of_line;
|
||||||
|
use super::OrgSource;
|
||||||
|
use crate::context::Matcher;
|
||||||
|
use crate::context::RefContext;
|
||||||
|
use crate::error::CustomError;
|
||||||
|
use crate::error::MyError;
|
||||||
|
use crate::error::Res;
|
||||||
|
use crate::parser::util::get_consumed;
|
||||||
|
use crate::parser::util::org_line_ending;
|
||||||
|
use crate::types::BabelCall;
|
||||||
|
|
||||||
|
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
|
||||||
|
pub(crate) fn babel_call<'b, 'g, 'r, 's>(
|
||||||
|
context: RefContext<'b, 'g, 'r, 's>,
|
||||||
|
input: OrgSource<'s>,
|
||||||
|
) -> Res<OrgSource<'s>, BabelCall<'s>> {
|
||||||
|
let (remaining, affiliated_keywords) = many0(affiliated_keyword)(input)?;
|
||||||
|
|
||||||
|
start_of_line(remaining)?;
|
||||||
|
let (remaining, _) = tuple((space0, tag("#+"), tag_no_case("call"), tag(":")))(remaining)?;
|
||||||
|
|
||||||
|
if let Ok((remaining, (_, line_break))) = tuple((space0, org_line_ending))(remaining) {
|
||||||
|
let (remaining, _trailing_ws) =
|
||||||
|
maybe_consume_trailing_whitespace_if_not_exiting(context, remaining)?;
|
||||||
|
let source = get_consumed(input, remaining);
|
||||||
|
return Ok((
|
||||||
|
remaining,
|
||||||
|
BabelCall {
|
||||||
|
source: Into::<&str>::into(source),
|
||||||
|
name: get_name(&affiliated_keywords),
|
||||||
|
value: Into::<&str>::into(line_break.take(0)),
|
||||||
|
call: None,
|
||||||
|
inside_header: None,
|
||||||
|
arguments: None,
|
||||||
|
end_header: None,
|
||||||
|
},
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
let (remaining, _ws) = space0(remaining)?;
|
||||||
|
let (remaining, (value, (call, inside_header, arguments, end_header))) =
|
||||||
|
consumed(babel_call_value)(remaining)?;
|
||||||
|
let (remaining, _ws) = tuple((space0, org_line_ending))(remaining)?;
|
||||||
|
|
||||||
|
let (remaining, _trailing_ws) =
|
||||||
|
maybe_consume_trailing_whitespace_if_not_exiting(context, remaining)?;
|
||||||
|
let source = get_consumed(input, remaining);
|
||||||
|
|
||||||
|
Ok((
|
||||||
|
remaining,
|
||||||
|
BabelCall {
|
||||||
|
source: Into::<&str>::into(source),
|
||||||
|
name: get_name(&affiliated_keywords),
|
||||||
|
value: Into::<&str>::into(value).trim_end(),
|
||||||
|
call: call.map(Into::<&str>::into),
|
||||||
|
inside_header: inside_header.map(Into::<&str>::into),
|
||||||
|
arguments: arguments.map(Into::<&str>::into),
|
||||||
|
end_header: end_header.map(Into::<&str>::into),
|
||||||
|
},
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
|
||||||
|
fn babel_call_value<'s>(
|
||||||
|
input: OrgSource<'s>,
|
||||||
|
) -> Res<
|
||||||
|
OrgSource<'s>,
|
||||||
|
(
|
||||||
|
Option<OrgSource<'s>>,
|
||||||
|
Option<OrgSource<'s>>,
|
||||||
|
Option<OrgSource<'s>>,
|
||||||
|
Option<OrgSource<'s>>,
|
||||||
|
),
|
||||||
|
> {
|
||||||
|
let (remaining, call) = opt(babel_call_call)(input)?;
|
||||||
|
let (remaining, inside_header) = opt(inside_header)(remaining)?;
|
||||||
|
let (remaining, arguments) = opt(arguments)(remaining)?;
|
||||||
|
let (remaining, end_header) = opt(end_header)(remaining)?;
|
||||||
|
Ok((
|
||||||
|
remaining,
|
||||||
|
(call, inside_header, arguments.flatten(), end_header),
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
|
||||||
|
fn babel_call_call<'s>(input: OrgSource<'s>) -> Res<OrgSource<'s>, OrgSource<'s>> {
|
||||||
|
verify(
|
||||||
|
recognize(many_till(
|
||||||
|
anychar,
|
||||||
|
alt((
|
||||||
|
peek(recognize(one_of("[("))),
|
||||||
|
recognize(tuple((space0, org_line_ending))),
|
||||||
|
)),
|
||||||
|
)),
|
||||||
|
|s| s.len() > 0,
|
||||||
|
)(input)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
|
||||||
|
fn inside_header<'s>(input: OrgSource<'s>) -> Res<OrgSource<'s>, OrgSource<'s>> {
|
||||||
|
let (remaining, contents) = balanced_bracket(
|
||||||
|
|i| tag("[")(i),
|
||||||
|
|i| peek(tag("]"))(i),
|
||||||
|
|i| recognize(tuple((space0, org_line_ending)))(i),
|
||||||
|
|i| tag("]")(i),
|
||||||
|
|s| s.get_bracket_depth(),
|
||||||
|
)(input)?;
|
||||||
|
let (contents_start, _) = tag("[")(input)?;
|
||||||
|
Ok((remaining, contents.unwrap_or(contents_start.take(0))))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
|
||||||
|
fn arguments<'s>(input: OrgSource<'s>) -> Res<OrgSource<'s>, Option<OrgSource<'s>>> {
|
||||||
|
balanced_bracket(
|
||||||
|
|i| tag("(")(i),
|
||||||
|
|i| peek(tag(")"))(i),
|
||||||
|
|i| recognize(tuple((space0, org_line_ending)))(i),
|
||||||
|
|i| tag(")")(i),
|
||||||
|
|s| s.get_parenthesis_depth(),
|
||||||
|
)(input)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
|
||||||
|
fn end_header<'s>(input: OrgSource<'s>) -> Res<OrgSource<'s>, OrgSource<'s>> {
|
||||||
|
let (remaining, _) = space0(input)?;
|
||||||
|
verify(
|
||||||
|
recognize(many_till(anychar, peek(tuple((space0, org_line_ending))))),
|
||||||
|
|s| s.len() > 0,
|
||||||
|
)(remaining)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn balanced_bracket<
|
||||||
|
O: Matcher,
|
||||||
|
S: Matcher,
|
||||||
|
F: Matcher,
|
||||||
|
E: Matcher,
|
||||||
|
D: for<'ss> Fn(OrgSource<'ss>) -> BracketDepth,
|
||||||
|
>(
|
||||||
|
opening_parser: O,
|
||||||
|
stop_parser: S,
|
||||||
|
fail_parser: F,
|
||||||
|
end_parser: E,
|
||||||
|
depth_function: D,
|
||||||
|
) -> impl for<'s> Fn(OrgSource<'s>) -> Res<OrgSource<'s>, Option<OrgSource<'s>>> {
|
||||||
|
move |input| {
|
||||||
|
impl_balanced_bracket::<&O, &S, &F, &E, &D>(
|
||||||
|
input,
|
||||||
|
&opening_parser,
|
||||||
|
&stop_parser,
|
||||||
|
&fail_parser,
|
||||||
|
&end_parser,
|
||||||
|
&depth_function,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn impl_balanced_bracket<
|
||||||
|
's,
|
||||||
|
O: Matcher,
|
||||||
|
S: Matcher,
|
||||||
|
F: Matcher,
|
||||||
|
E: Matcher,
|
||||||
|
D: for<'ss> Fn(OrgSource<'ss>) -> BracketDepth,
|
||||||
|
>(
|
||||||
|
input: OrgSource<'s>,
|
||||||
|
opening_parser: O,
|
||||||
|
stop_parser: S,
|
||||||
|
fail_parser: F,
|
||||||
|
end_parser: E,
|
||||||
|
depth_function: D,
|
||||||
|
) -> Res<OrgSource<'s>, Option<OrgSource<'s>>> {
|
||||||
|
let (mut remaining, _) = opening_parser(input)?;
|
||||||
|
let contents_start = remaining;
|
||||||
|
let original_depth = depth_function(remaining);
|
||||||
|
|
||||||
|
loop {
|
||||||
|
let bracket_depth = depth_function(remaining);
|
||||||
|
if bracket_depth == original_depth {
|
||||||
|
let (remain, stop_result) = opt(&stop_parser)(remaining)?;
|
||||||
|
remaining = remain;
|
||||||
|
if stop_result.is_some() {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if fail_parser(remaining).is_ok() {
|
||||||
|
return Err(nom::Err::Error(CustomError::MyError(MyError(
|
||||||
|
"Fail parser matched.",
|
||||||
|
))));
|
||||||
|
}
|
||||||
|
|
||||||
|
let (remain, _) = anychar(remaining)?;
|
||||||
|
remaining = remain;
|
||||||
|
}
|
||||||
|
let contents_end = remaining;
|
||||||
|
|
||||||
|
let (remaining, _) = end_parser(remaining)?;
|
||||||
|
let contents = if contents_start != contents_end {
|
||||||
|
Some(contents_start.get_until(contents_end))
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
};
|
||||||
|
Ok((remaining, contents))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use nom::combinator::opt;
|
||||||
|
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn simple_call() -> Result<(), Box<dyn std::error::Error>> {
|
||||||
|
let input = OrgSource::new("()");
|
||||||
|
let (remaining, call) = opt(babel_call_call)(input)?;
|
||||||
|
assert_eq!(Into::<&str>::into(remaining), "()");
|
||||||
|
assert_eq!(call, None);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,23 +1,28 @@
|
|||||||
use nom::branch::alt;
|
use nom::branch::alt;
|
||||||
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::digit1;
|
use nom::character::complete::digit1;
|
||||||
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::map;
|
||||||
|
use nom::combinator::opt;
|
||||||
use nom::combinator::recognize;
|
use nom::combinator::recognize;
|
||||||
use nom::combinator::verify;
|
|
||||||
use nom::sequence::tuple;
|
use nom::sequence::tuple;
|
||||||
|
|
||||||
use super::org_source::OrgSource;
|
use super::org_source::OrgSource;
|
||||||
|
use super::timestamp::inactive_date_range_timestamp;
|
||||||
|
use super::timestamp::inactive_time_range_timestamp;
|
||||||
|
use super::timestamp::inactive_timestamp;
|
||||||
|
use super::util::maybe_consume_trailing_whitespace_if_not_exiting;
|
||||||
|
use super::util::org_line_ending;
|
||||||
use crate::context::parser_with_context;
|
use crate::context::parser_with_context;
|
||||||
use crate::context::RefContext;
|
use crate::context::RefContext;
|
||||||
use crate::error::Res;
|
use crate::error::Res;
|
||||||
use crate::parser::util::get_consumed;
|
use crate::parser::util::get_consumed;
|
||||||
use crate::parser::util::start_of_line;
|
use crate::parser::util::start_of_line;
|
||||||
use crate::types::Clock;
|
use crate::types::Clock;
|
||||||
|
use crate::types::ClockStatus;
|
||||||
|
use crate::types::Timestamp;
|
||||||
|
|
||||||
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
|
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
|
||||||
pub(crate) fn clock<'b, 'g, 'r, 's>(
|
pub(crate) fn clock<'b, 'g, 'r, 's>(
|
||||||
@@ -29,52 +34,56 @@ pub(crate) fn clock<'b, 'g, 'r, 's>(
|
|||||||
let (remaining, _clock) = tag_no_case("clock:")(remaining)?;
|
let (remaining, _clock) = tag_no_case("clock:")(remaining)?;
|
||||||
let (remaining, _gap_whitespace) = space1(remaining)?;
|
let (remaining, _gap_whitespace) = space1(remaining)?;
|
||||||
|
|
||||||
let (remaining, _timestamp_junk) = alt((
|
let (remaining, (timestamp, duration)) = clock_timestamp(context, remaining)?;
|
||||||
parser_with_context!(inactive_timestamp_range_duration)(context),
|
let (remaining, _) = tuple((space0, org_line_ending))(remaining)?;
|
||||||
parser_with_context!(inactive_timestamp)(context),
|
|
||||||
))(remaining)?;
|
|
||||||
|
|
||||||
|
let (remaining, _trailing_ws) =
|
||||||
|
maybe_consume_trailing_whitespace_if_not_exiting(context, remaining)?;
|
||||||
let source = get_consumed(input, remaining);
|
let source = get_consumed(input, remaining);
|
||||||
Ok((
|
Ok((
|
||||||
remaining,
|
remaining,
|
||||||
Clock {
|
Clock {
|
||||||
source: source.into(),
|
source: source.into(),
|
||||||
|
timestamp,
|
||||||
|
duration,
|
||||||
|
status: if duration.is_some() {
|
||||||
|
ClockStatus::Closed
|
||||||
|
} else {
|
||||||
|
ClockStatus::Running
|
||||||
|
},
|
||||||
},
|
},
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
|
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
|
||||||
fn inactive_timestamp_range_duration<'b, 'g, 'r, 's>(
|
fn clock_timestamp<'b, 'g, 'r, 's>(
|
||||||
_context: RefContext<'b, 'g, 'r, 's>,
|
context: RefContext<'b, 'g, 'r, 's>,
|
||||||
input: OrgSource<'s>,
|
input: OrgSource<'s>,
|
||||||
) -> Res<OrgSource<'s>, OrgSource<'s>> {
|
) -> Res<OrgSource<'s>, (Timestamp<'s>, Option<&'s str>)> {
|
||||||
recognize(tuple((
|
// TODO: This would be more efficient if we didn't throw away the parse result of the first half of an active/inactive date range timestamp if the parse fails (as in, the first thing active_date_range_timestamp parses is a active_timestamp but then we throw that away if it doesn't turn out to be a full active_date_range_timestamp despite the active_timestamp parse being completely valid). I am going with the simplest/cleanest approach for the first implementation.
|
||||||
tag("["),
|
alt((
|
||||||
is_not("\r\n]"),
|
// Order matters here. If its a date range, we need to parse the entire date range instead of just the first timestamp. If its a time range, we need to make sure thats parsed as a time range instead of as the "rest" portion of a single timestamp.
|
||||||
tag("]--["),
|
map(
|
||||||
is_not("\r\n]"),
|
parser_with_context!(inactive_time_range_timestamp)(context),
|
||||||
tag("]"),
|
|timestamp| (timestamp, None),
|
||||||
space1,
|
),
|
||||||
tag("=>"),
|
map(
|
||||||
space1,
|
tuple((
|
||||||
digit1,
|
parser_with_context!(inactive_date_range_timestamp)(context),
|
||||||
tag(":"),
|
opt(duration),
|
||||||
verify(digit1, |mm: &OrgSource<'_>| mm.len() == 2),
|
)),
|
||||||
space0,
|
|(timestamp, duration)| (timestamp, duration.map(Into::<&str>::into)),
|
||||||
alt((line_ending, eof)),
|
),
|
||||||
)))(input)
|
map(
|
||||||
|
parser_with_context!(inactive_timestamp)(context),
|
||||||
|
|timestamp| (timestamp, None),
|
||||||
|
),
|
||||||
|
))(input)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
|
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
|
||||||
fn inactive_timestamp<'b, 'g, 'r, 's>(
|
fn duration<'s>(input: OrgSource<'s>) -> Res<OrgSource<'s>, OrgSource<'s>> {
|
||||||
_context: RefContext<'b, 'g, 'r, 's>,
|
let (remaining, _) = tuple((tag("=>"), space1))(input)?;
|
||||||
input: OrgSource<'s>,
|
let (remaining, duration) = recognize(tuple((digit1, tag(":"), digit1)))(remaining)?;
|
||||||
) -> Res<OrgSource<'s>, OrgSource<'s>> {
|
Ok((remaining, duration))
|
||||||
recognize(tuple((
|
|
||||||
tag("["),
|
|
||||||
is_not("\r\n]"),
|
|
||||||
tag("]"),
|
|
||||||
space0,
|
|
||||||
alt((line_ending, eof)),
|
|
||||||
)))(input)
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ use nom::sequence::tuple;
|
|||||||
|
|
||||||
use super::org_source::OrgSource;
|
use super::org_source::OrgSource;
|
||||||
use super::util::get_consumed;
|
use super::util::get_consumed;
|
||||||
|
use super::util::maybe_consume_trailing_whitespace_if_not_exiting;
|
||||||
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::ContextElement;
|
use crate::context::ContextElement;
|
||||||
@@ -43,6 +44,8 @@ pub(crate) fn comment<'b, 'g, 'r, 's>(
|
|||||||
let (remaining, mut remaining_lines) =
|
let (remaining, mut remaining_lines) =
|
||||||
many0(preceded(not(exit_matcher), comment_line_matcher))(remaining)?;
|
many0(preceded(not(exit_matcher), comment_line_matcher))(remaining)?;
|
||||||
|
|
||||||
|
let (remaining, _trailing_ws) =
|
||||||
|
maybe_consume_trailing_whitespace_if_not_exiting(context, remaining)?;
|
||||||
let source = get_consumed(input, remaining);
|
let source = get_consumed(input, remaining);
|
||||||
let mut value = Vec::with_capacity(remaining_lines.len() + 1);
|
let mut value = Vec::with_capacity(remaining_lines.len() + 1);
|
||||||
let last_line = remaining_lines.pop();
|
let last_line = remaining_lines.pop();
|
||||||
|
|||||||
@@ -1,11 +1,14 @@
|
|||||||
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::character::complete::line_ending;
|
use nom::combinator::recognize;
|
||||||
use nom::combinator::eof;
|
use nom::multi::many0;
|
||||||
use nom::sequence::tuple;
|
use nom::sequence::tuple;
|
||||||
|
|
||||||
|
use super::keyword::affiliated_keyword;
|
||||||
use super::org_source::OrgSource;
|
use super::org_source::OrgSource;
|
||||||
|
use super::util::get_name;
|
||||||
|
use super::util::maybe_consume_trailing_whitespace_if_not_exiting;
|
||||||
|
use super::util::org_line_ending;
|
||||||
use crate::context::RefContext;
|
use crate::context::RefContext;
|
||||||
use crate::error::Res;
|
use crate::error::Res;
|
||||||
use crate::parser::util::get_consumed;
|
use crate::parser::util::get_consumed;
|
||||||
@@ -14,25 +17,30 @@ use crate::types::DiarySexp;
|
|||||||
|
|
||||||
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
|
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
|
||||||
pub(crate) fn diary_sexp<'b, 'g, 'r, 's>(
|
pub(crate) fn diary_sexp<'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>, DiarySexp<'s>> {
|
) -> Res<OrgSource<'s>, DiarySexp<'s>> {
|
||||||
start_of_line(input)?;
|
let (remaining, affiliated_keywords) = many0(affiliated_keyword)(input)?;
|
||||||
let (remaining, _clock) = tag("%%(")(input)?;
|
start_of_line(remaining)?;
|
||||||
let (remaining, _contents) = is_not("\r\n")(remaining)?;
|
let (remaining, value) = recognize(tuple((tag("%%("), is_not("\r\n"))))(remaining)?;
|
||||||
let (remaining, _eol) = alt((line_ending, eof))(remaining)?;
|
let (remaining, _eol) = org_line_ending(remaining)?;
|
||||||
|
|
||||||
|
let (remaining, _trailing_ws) =
|
||||||
|
maybe_consume_trailing_whitespace_if_not_exiting(context, remaining)?;
|
||||||
let source = get_consumed(input, remaining);
|
let source = get_consumed(input, remaining);
|
||||||
Ok((
|
Ok((
|
||||||
remaining,
|
remaining,
|
||||||
DiarySexp {
|
DiarySexp {
|
||||||
source: source.into(),
|
source: source.into(),
|
||||||
|
name: get_name(&affiliated_keywords),
|
||||||
|
value: Into::<&str>::into(value),
|
||||||
},
|
},
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
|
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
|
||||||
pub(crate) fn detect_diary_sexp<'s>(input: OrgSource<'s>) -> Res<OrgSource<'s>, ()> {
|
pub(crate) fn detect_diary_sexp<'s>(input: OrgSource<'s>) -> Res<OrgSource<'s>, ()> {
|
||||||
|
let (input, _) = many0(affiliated_keyword)(input)?;
|
||||||
tuple((start_of_line, tag("%%(")))(input)?;
|
tuple((start_of_line, tag("%%(")))(input)?;
|
||||||
Ok((input, ()))
|
Ok((input, ()))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -131,7 +131,8 @@ fn document_org_source<'b, 'g, 'r, 's>(
|
|||||||
.collect::<Result<Vec<_>, _>>()?;
|
.collect::<Result<Vec<_>, _>>()?;
|
||||||
for setup_file in setup_files.iter().map(String::as_str) {
|
for setup_file in setup_files.iter().map(String::as_str) {
|
||||||
let (_, setup_file_settings) =
|
let (_, setup_file_settings) =
|
||||||
scan_for_in_buffer_settings(setup_file.into()).map_err(|_err| {
|
scan_for_in_buffer_settings(setup_file.into()).map_err(|err| {
|
||||||
|
eprintln!("{}", err);
|
||||||
nom::Err::Error(CustomError::MyError(MyError(
|
nom::Err::Error(CustomError::MyError(MyError(
|
||||||
"TODO: make this take an owned string so I can dump err.to_string() into it."
|
"TODO: make this take an owned string so I can dump err.to_string() into it."
|
||||||
.into(),
|
.into(),
|
||||||
@@ -141,7 +142,8 @@ fn document_org_source<'b, 'g, 'r, 's>(
|
|||||||
}
|
}
|
||||||
final_settings.extend(document_settings);
|
final_settings.extend(document_settings);
|
||||||
let new_settings = apply_in_buffer_settings(final_settings, context.get_global_settings())
|
let new_settings = apply_in_buffer_settings(final_settings, context.get_global_settings())
|
||||||
.map_err(|_err| {
|
.map_err(|err| {
|
||||||
|
eprintln!("{}", err);
|
||||||
nom::Err::Error(CustomError::MyError(MyError(
|
nom::Err::Error(CustomError::MyError(MyError(
|
||||||
"TODO: make this take an owned string so I can dump err.to_string() into it."
|
"TODO: make this take an owned string so I can dump err.to_string() into it."
|
||||||
.into(),
|
.into(),
|
||||||
|
|||||||
@@ -7,10 +7,14 @@ use nom::character::complete::space0;
|
|||||||
use nom::combinator::eof;
|
use nom::combinator::eof;
|
||||||
use nom::combinator::not;
|
use nom::combinator::not;
|
||||||
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::tuple;
|
use nom::sequence::tuple;
|
||||||
|
|
||||||
|
use super::keyword::affiliated_keyword;
|
||||||
use super::org_source::OrgSource;
|
use super::org_source::OrgSource;
|
||||||
|
use super::util::get_name;
|
||||||
|
use super::util::maybe_consume_trailing_whitespace_if_not_exiting;
|
||||||
use crate::context::parser_with_context;
|
use crate::context::parser_with_context;
|
||||||
use crate::context::ContextElement;
|
use crate::context::ContextElement;
|
||||||
use crate::context::ExitClass;
|
use crate::context::ExitClass;
|
||||||
@@ -41,8 +45,9 @@ pub(crate) fn drawer<'b, 'g, 'r, 's>(
|
|||||||
"Cannot nest objects of the same element".into(),
|
"Cannot nest objects of the same element".into(),
|
||||||
))));
|
))));
|
||||||
}
|
}
|
||||||
start_of_line(input)?;
|
let (remaining, affiliated_keywords) = many0(affiliated_keyword)(input)?;
|
||||||
let (remaining, _leading_whitespace) = space0(input)?;
|
start_of_line(remaining)?;
|
||||||
|
let (remaining, _leading_whitespace) = space0(remaining)?;
|
||||||
let (remaining, (_open_colon, drawer_name, _close_colon, _new_line)) = tuple((
|
let (remaining, (_open_colon, drawer_name, _close_colon, _new_line)) = tuple((
|
||||||
tag(":"),
|
tag(":"),
|
||||||
name,
|
name,
|
||||||
@@ -84,13 +89,16 @@ pub(crate) fn drawer<'b, 'g, 'r, 's>(
|
|||||||
};
|
};
|
||||||
let (remaining, _end) = drawer_end(&parser_context, remaining)?;
|
let (remaining, _end) = drawer_end(&parser_context, remaining)?;
|
||||||
|
|
||||||
|
let (remaining, _trailing_ws) =
|
||||||
|
maybe_consume_trailing_whitespace_if_not_exiting(context, remaining)?;
|
||||||
let source = get_consumed(input, remaining);
|
let source = get_consumed(input, remaining);
|
||||||
|
|
||||||
Ok((
|
Ok((
|
||||||
remaining,
|
remaining,
|
||||||
Drawer {
|
Drawer {
|
||||||
source: source.into(),
|
source: source.into(),
|
||||||
name: drawer_name.into(),
|
name: get_name(&affiliated_keywords),
|
||||||
|
drawer_name: drawer_name.into(),
|
||||||
children,
|
children,
|
||||||
},
|
},
|
||||||
))
|
))
|
||||||
|
|||||||
@@ -17,7 +17,10 @@ use nom::multi::many_till;
|
|||||||
use nom::sequence::preceded;
|
use nom::sequence::preceded;
|
||||||
use nom::sequence::tuple;
|
use nom::sequence::tuple;
|
||||||
|
|
||||||
|
use super::keyword::affiliated_keyword;
|
||||||
use super::org_source::OrgSource;
|
use super::org_source::OrgSource;
|
||||||
|
use super::util::get_name;
|
||||||
|
use super::util::maybe_consume_trailing_whitespace_if_not_exiting;
|
||||||
use crate::context::parser_with_context;
|
use crate::context::parser_with_context;
|
||||||
use crate::context::ContextElement;
|
use crate::context::ContextElement;
|
||||||
use crate::context::ExitClass;
|
use crate::context::ExitClass;
|
||||||
@@ -47,8 +50,10 @@ pub(crate) fn dynamic_block<'b, 'g, 'r, 's>(
|
|||||||
"Cannot nest objects of the same element".into(),
|
"Cannot nest objects of the same element".into(),
|
||||||
))));
|
))));
|
||||||
}
|
}
|
||||||
start_of_line(input)?;
|
let (remaining, affiliated_keywords) = many0(affiliated_keyword)(input)?;
|
||||||
let (remaining, _leading_whitespace) = space0(input)?;
|
|
||||||
|
start_of_line(remaining)?;
|
||||||
|
let (remaining, _leading_whitespace) = space0(remaining)?;
|
||||||
let (remaining, (_, name, parameters, _, _)) = tuple((
|
let (remaining, (_, name, parameters, _, _)) = tuple((
|
||||||
recognize(tuple((tag_no_case("#+begin:"), space1))),
|
recognize(tuple((tag_no_case("#+begin:"), space1))),
|
||||||
name,
|
name,
|
||||||
@@ -92,12 +97,15 @@ pub(crate) fn dynamic_block<'b, 'g, 'r, 's>(
|
|||||||
|
|
||||||
let (remaining, _end) = dynamic_block_end(&parser_context, remaining)?;
|
let (remaining, _end) = dynamic_block_end(&parser_context, remaining)?;
|
||||||
|
|
||||||
|
let (remaining, _trailing_ws) =
|
||||||
|
maybe_consume_trailing_whitespace_if_not_exiting(context, remaining)?;
|
||||||
let source = get_consumed(input, remaining);
|
let source = get_consumed(input, remaining);
|
||||||
Ok((
|
Ok((
|
||||||
remaining,
|
remaining,
|
||||||
DynamicBlock {
|
DynamicBlock {
|
||||||
source: source.into(),
|
source: source.into(),
|
||||||
name: name.into(),
|
name: get_name(&affiliated_keywords),
|
||||||
|
block_name: name.into(),
|
||||||
parameters: parameters.map(|val| val.into()),
|
parameters: parameters.map(|val| val.into()),
|
||||||
children,
|
children,
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -1,7 +1,12 @@
|
|||||||
use nom::branch::alt;
|
use nom::branch::alt;
|
||||||
use nom::combinator::map;
|
use nom::combinator::map;
|
||||||
use nom::multi::many0;
|
use nom::combinator::opt;
|
||||||
|
use nom::combinator::peek;
|
||||||
|
use nom::sequence::tuple;
|
||||||
|
#[cfg(feature = "tracing")]
|
||||||
|
use tracing::span;
|
||||||
|
|
||||||
|
use super::babel_call::babel_call;
|
||||||
use super::clock::clock;
|
use super::clock::clock;
|
||||||
use super::comment::comment;
|
use super::comment::comment;
|
||||||
use super::comment::detect_comment;
|
use super::comment::detect_comment;
|
||||||
@@ -16,7 +21,7 @@ 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::affiliated_keyword;
|
||||||
use super::keyword::babel_call_keyword;
|
use super::keyword::affiliated_keyword_as_regular_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;
|
||||||
@@ -29,8 +34,6 @@ use super::paragraph::paragraph;
|
|||||||
use super::plain_list::detect_plain_list;
|
use super::plain_list::detect_plain_list;
|
||||||
use super::plain_list::plain_list;
|
use super::plain_list::plain_list;
|
||||||
use super::table::detect_table;
|
use super::table::detect_table;
|
||||||
use super::util::get_consumed;
|
|
||||||
use super::util::maybe_consume_trailing_whitespace_if_not_exiting;
|
|
||||||
use crate::context::parser_with_context;
|
use crate::context::parser_with_context;
|
||||||
use crate::context::RefContext;
|
use crate::context::RefContext;
|
||||||
use crate::error::CustomError;
|
use crate::error::CustomError;
|
||||||
@@ -38,7 +41,6 @@ use crate::error::MyError;
|
|||||||
use crate::error::Res;
|
use crate::error::Res;
|
||||||
use crate::parser::table::org_mode_table;
|
use crate::parser::table::org_mode_table;
|
||||||
use crate::types::Element;
|
use crate::types::Element;
|
||||||
use crate::types::SetSource;
|
|
||||||
|
|
||||||
pub(crate) const fn element(
|
pub(crate) const fn element(
|
||||||
can_be_paragraph: bool,
|
can_be_paragraph: bool,
|
||||||
@@ -72,58 +74,98 @@ fn _element<'b, 'g, '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 babel_keyword_matcher = parser_with_context!(babel_call)(context);
|
||||||
let babel_keyword_matcher = parser_with_context!(babel_call_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 (mut remaining, mut maybe_element) = {
|
||||||
let (remaining, mut affiliated_keywords) = many0(affiliated_keyword_matcher)(input)?;
|
#[cfg(feature = "tracing")]
|
||||||
let (remaining, mut element) = match alt((
|
let span = span!(tracing::Level::DEBUG, "Main element block");
|
||||||
map(plain_list_matcher, Element::PlainList),
|
#[cfg(feature = "tracing")]
|
||||||
greater_block_matcher,
|
let _enter = span.enter();
|
||||||
map(dynamic_block_matcher, Element::DynamicBlock),
|
|
||||||
map(footnote_definition_matcher, Element::FootnoteDefinition),
|
opt(alt((
|
||||||
map(comment_matcher, Element::Comment),
|
map(plain_list_matcher, Element::PlainList),
|
||||||
map(drawer_matcher, Element::Drawer),
|
greater_block_matcher,
|
||||||
map(table_matcher, Element::Table),
|
map(dynamic_block_matcher, Element::DynamicBlock),
|
||||||
map(verse_block_matcher, Element::VerseBlock),
|
map(footnote_definition_matcher, Element::FootnoteDefinition),
|
||||||
map(comment_block_matcher, Element::CommentBlock),
|
map(comment_matcher, Element::Comment),
|
||||||
map(example_block_matcher, Element::ExampleBlock),
|
map(drawer_matcher, Element::Drawer),
|
||||||
map(export_block_matcher, Element::ExportBlock),
|
map(table_matcher, Element::Table),
|
||||||
map(src_block_matcher, Element::SrcBlock),
|
map(verse_block_matcher, Element::VerseBlock),
|
||||||
map(clock_matcher, Element::Clock),
|
map(comment_block_matcher, Element::CommentBlock),
|
||||||
map(diary_sexp_matcher, Element::DiarySexp),
|
map(example_block_matcher, Element::ExampleBlock),
|
||||||
map(fixed_width_area_matcher, Element::FixedWidthArea),
|
map(export_block_matcher, Element::ExportBlock),
|
||||||
map(horizontal_rule_matcher, Element::HorizontalRule),
|
map(src_block_matcher, Element::SrcBlock),
|
||||||
map(latex_environment_matcher, Element::LatexEnvironment),
|
map(clock_matcher, Element::Clock),
|
||||||
map(babel_keyword_matcher, Element::BabelCall),
|
map(diary_sexp_matcher, Element::DiarySexp),
|
||||||
map(keyword_matcher, Element::Keyword),
|
map(fixed_width_area_matcher, Element::FixedWidthArea),
|
||||||
))(remaining)
|
map(horizontal_rule_matcher, Element::HorizontalRule),
|
||||||
{
|
map(latex_environment_matcher, Element::LatexEnvironment),
|
||||||
the_ok @ Ok(_) => the_ok,
|
map(babel_keyword_matcher, Element::BabelCall),
|
||||||
Err(_) => {
|
map(keyword_matcher, Element::Keyword),
|
||||||
if can_be_paragraph {
|
)))(input)?
|
||||||
match map(paragraph_matcher, Element::Paragraph)(remaining) {
|
};
|
||||||
the_ok @ Ok(_) => the_ok,
|
|
||||||
Err(_) => {
|
if maybe_element.is_none() && can_be_paragraph {
|
||||||
// TODO: Because this function expects a single element, if there are multiple affiliated keywords before an element that cannot have affiliated keywords, we end up re-parsing the affiliated keywords many times.
|
#[cfg(feature = "tracing")]
|
||||||
affiliated_keywords.clear();
|
let span = span!(tracing::Level::DEBUG, "Paragraph with affiliated keyword.");
|
||||||
map(affiliated_keyword_matcher, Element::Keyword)(input)
|
#[cfg(feature = "tracing")]
|
||||||
}
|
let _enter = span.enter();
|
||||||
}
|
|
||||||
} else {
|
let (remain, paragraph_with_affiliated_keyword) = opt(map(
|
||||||
affiliated_keywords.clear();
|
tuple((
|
||||||
map(affiliated_keyword_matcher, Element::Keyword)(input)
|
peek(affiliated_keyword),
|
||||||
}
|
map(paragraph_matcher, Element::Paragraph),
|
||||||
|
)),
|
||||||
|
|(_, paragraph)| paragraph,
|
||||||
|
))(remaining)?;
|
||||||
|
if paragraph_with_affiliated_keyword.is_some() {
|
||||||
|
remaining = remain;
|
||||||
|
maybe_element = paragraph_with_affiliated_keyword;
|
||||||
}
|
}
|
||||||
}?;
|
}
|
||||||
|
|
||||||
let (remaining, _trailing_ws) =
|
if maybe_element.is_none() {
|
||||||
maybe_consume_trailing_whitespace_if_not_exiting(context, remaining)?;
|
#[cfg(feature = "tracing")]
|
||||||
|
let span = span!(
|
||||||
|
tracing::Level::DEBUG,
|
||||||
|
"Affiliated keyword as regular keyword."
|
||||||
|
);
|
||||||
|
#[cfg(feature = "tracing")]
|
||||||
|
let _enter = span.enter();
|
||||||
|
|
||||||
let source = get_consumed(input, remaining);
|
let (remain, kw) = opt(map(
|
||||||
element.set_source(source.into());
|
parser_with_context!(affiliated_keyword_as_regular_keyword)(context),
|
||||||
|
Element::Keyword,
|
||||||
|
))(remaining)?;
|
||||||
|
if kw.is_some() {
|
||||||
|
maybe_element = kw;
|
||||||
|
remaining = remain;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if maybe_element.is_none() && can_be_paragraph {
|
||||||
|
#[cfg(feature = "tracing")]
|
||||||
|
let span = span!(
|
||||||
|
tracing::Level::DEBUG,
|
||||||
|
"Paragraph without affiliated keyword."
|
||||||
|
);
|
||||||
|
#[cfg(feature = "tracing")]
|
||||||
|
let _enter = span.enter();
|
||||||
|
|
||||||
|
let (remain, paragraph_without_affiliated_keyword) =
|
||||||
|
map(paragraph_matcher, Element::Paragraph)(remaining)?;
|
||||||
|
remaining = remain;
|
||||||
|
maybe_element = Some(paragraph_without_affiliated_keyword);
|
||||||
|
}
|
||||||
|
|
||||||
|
if maybe_element.is_none() {
|
||||||
|
return Err(nom::Err::Error(CustomError::MyError(MyError(
|
||||||
|
"No element.",
|
||||||
|
))));
|
||||||
|
}
|
||||||
|
let element = maybe_element.expect("The above if-statement ensures this is Some().");
|
||||||
|
|
||||||
Ok((remaining, element))
|
Ok((remaining, element))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,17 +1,18 @@
|
|||||||
use nom::branch::alt;
|
use nom::branch::alt;
|
||||||
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::anychar;
|
||||||
use nom::character::complete::space0;
|
use nom::character::complete::space0;
|
||||||
use nom::combinator::eof;
|
|
||||||
use nom::combinator::not;
|
use nom::combinator::not;
|
||||||
use nom::combinator::recognize;
|
use nom::combinator::recognize;
|
||||||
use nom::multi::many0;
|
use nom::multi::many0;
|
||||||
|
use nom::multi::many_till;
|
||||||
use nom::sequence::preceded;
|
use nom::sequence::preceded;
|
||||||
use nom::sequence::tuple;
|
use nom::sequence::tuple;
|
||||||
|
|
||||||
|
use super::keyword::affiliated_keyword;
|
||||||
use super::org_source::OrgSource;
|
use super::org_source::OrgSource;
|
||||||
use super::util::only_space1;
|
use super::util::get_name;
|
||||||
|
use super::util::maybe_consume_trailing_whitespace_if_not_exiting;
|
||||||
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;
|
||||||
@@ -26,17 +27,35 @@ pub(crate) fn fixed_width_area<'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>, FixedWidthArea<'s>> {
|
) -> Res<OrgSource<'s>, FixedWidthArea<'s>> {
|
||||||
|
let (remaining, affiliated_keywords) = many0(affiliated_keyword)(input)?;
|
||||||
let fixed_width_area_line_matcher = parser_with_context!(fixed_width_area_line)(context);
|
let fixed_width_area_line_matcher = parser_with_context!(fixed_width_area_line)(context);
|
||||||
let exit_matcher = parser_with_context!(exit_matcher_parser)(context);
|
let exit_matcher = parser_with_context!(exit_matcher_parser)(context);
|
||||||
let (remaining, _first_line) = fixed_width_area_line_matcher(input)?;
|
let (remaining, first_line) = fixed_width_area_line_matcher(remaining)?;
|
||||||
let (remaining, _remaining_lines) =
|
let (remaining, mut remaining_lines) =
|
||||||
many0(preceded(not(exit_matcher), fixed_width_area_line_matcher))(remaining)?;
|
many0(preceded(not(exit_matcher), fixed_width_area_line_matcher))(remaining)?;
|
||||||
|
|
||||||
|
let (remaining, _trailing_ws) =
|
||||||
|
maybe_consume_trailing_whitespace_if_not_exiting(context, remaining)?;
|
||||||
let source = get_consumed(input, remaining);
|
let source = get_consumed(input, remaining);
|
||||||
|
let mut value = Vec::with_capacity(remaining_lines.len() + 1);
|
||||||
|
let last_line = remaining_lines.pop();
|
||||||
|
if let Some(last_line) = last_line {
|
||||||
|
value.push(Into::<&str>::into(first_line));
|
||||||
|
value.extend(remaining_lines.into_iter().map(Into::<&str>::into));
|
||||||
|
let last_line = Into::<&str>::into(last_line);
|
||||||
|
// Trim the line ending from the final line.
|
||||||
|
value.push(&last_line[..(last_line.len() - 1)])
|
||||||
|
} else {
|
||||||
|
// Trim the line ending from the only line.
|
||||||
|
let only_line = Into::<&str>::into(first_line);
|
||||||
|
value.push(&only_line[..(only_line.len() - 1)])
|
||||||
|
}
|
||||||
Ok((
|
Ok((
|
||||||
remaining,
|
remaining,
|
||||||
FixedWidthArea {
|
FixedWidthArea {
|
||||||
source: source.into(),
|
source: source.into(),
|
||||||
|
name: get_name(&affiliated_keywords),
|
||||||
|
value,
|
||||||
},
|
},
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
@@ -47,23 +66,23 @@ fn fixed_width_area_line<'b, 'g, 'r, 's>(
|
|||||||
input: OrgSource<'s>,
|
input: OrgSource<'s>,
|
||||||
) -> Res<OrgSource<'s>, OrgSource<'s>> {
|
) -> Res<OrgSource<'s>, OrgSource<'s>> {
|
||||||
start_of_line(input)?;
|
start_of_line(input)?;
|
||||||
let (remaining, _indent) = space0(input)?;
|
let (remaining, _) = tuple((space0, tag(":")))(input)?;
|
||||||
let (remaining, _) = tuple((
|
if let Ok((remaining, line_break)) = org_line_ending(remaining) {
|
||||||
tag(":"),
|
return Ok((remaining, line_break));
|
||||||
alt((recognize(tuple((only_space1, is_not("\r\n")))), space0)),
|
}
|
||||||
org_line_ending,
|
let (remaining, _) = tag(" ")(remaining)?;
|
||||||
))(remaining)?;
|
let (remaining, value) = recognize(many_till(anychar, org_line_ending))(remaining)?;
|
||||||
let source = get_consumed(input, remaining);
|
Ok((remaining, value))
|
||||||
Ok((remaining, source))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
|
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
|
||||||
pub(crate) fn detect_fixed_width_area<'s>(input: OrgSource<'s>) -> Res<OrgSource<'s>, ()> {
|
pub(crate) fn detect_fixed_width_area<'s>(input: OrgSource<'s>) -> Res<OrgSource<'s>, ()> {
|
||||||
|
let (input, _) = many0(affiliated_keyword)(input)?;
|
||||||
tuple((
|
tuple((
|
||||||
start_of_line,
|
start_of_line,
|
||||||
space0,
|
space0,
|
||||||
tag(":"),
|
tag(":"),
|
||||||
alt((tag(" "), line_ending, eof)),
|
alt((tag(" "), org_line_ending)),
|
||||||
))(input)?;
|
))(input)?;
|
||||||
Ok((input, ()))
|
Ok((input, ()))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,8 +11,11 @@ use nom::multi::many1;
|
|||||||
use nom::multi::many_till;
|
use nom::multi::many_till;
|
||||||
use nom::sequence::tuple;
|
use nom::sequence::tuple;
|
||||||
|
|
||||||
|
use super::keyword::affiliated_keyword;
|
||||||
use super::org_source::OrgSource;
|
use super::org_source::OrgSource;
|
||||||
|
use super::util::get_name;
|
||||||
use super::util::include_input;
|
use super::util::include_input;
|
||||||
|
use super::util::maybe_consume_trailing_whitespace_if_not_exiting;
|
||||||
use super::util::WORD_CONSTITUENT_CHARACTERS;
|
use super::util::WORD_CONSTITUENT_CHARACTERS;
|
||||||
use crate::context::parser_with_context;
|
use crate::context::parser_with_context;
|
||||||
use crate::context::ContextElement;
|
use crate::context::ContextElement;
|
||||||
@@ -41,7 +44,8 @@ pub(crate) fn footnote_definition<'b, 'g, 'r, 's>(
|
|||||||
"Cannot nest objects of the same element".into(),
|
"Cannot nest objects of the same element".into(),
|
||||||
))));
|
))));
|
||||||
}
|
}
|
||||||
start_of_line(input)?;
|
let (remaining, affiliated_keywords) = many0(affiliated_keyword)(input)?;
|
||||||
|
start_of_line(remaining)?;
|
||||||
// Cannot be indented.
|
// Cannot be indented.
|
||||||
let (remaining, (_, lbl, _, _, _)) = tuple((
|
let (remaining, (_, lbl, _, _, _)) = tuple((
|
||||||
tag_no_case("[fn:"),
|
tag_no_case("[fn:"),
|
||||||
@@ -51,7 +55,7 @@ pub(crate) fn footnote_definition<'b, 'g, 'r, 's>(
|
|||||||
opt(verify(many0(blank_line), |lines: &Vec<OrgSource<'_>>| {
|
opt(verify(many0(blank_line), |lines: &Vec<OrgSource<'_>>| {
|
||||||
lines.len() <= 2
|
lines.len() <= 2
|
||||||
})),
|
})),
|
||||||
))(input)?;
|
))(remaining)?;
|
||||||
let contexts = [
|
let contexts = [
|
||||||
ContextElement::ConsumeTrailingWhitespace(true),
|
ContextElement::ConsumeTrailingWhitespace(true),
|
||||||
ContextElement::Context("footnote definition"),
|
ContextElement::Context("footnote definition"),
|
||||||
@@ -80,11 +84,14 @@ pub(crate) fn footnote_definition<'b, 'g, 'r, 's>(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let (remaining, _trailing_ws) =
|
||||||
|
maybe_consume_trailing_whitespace_if_not_exiting(context, remaining)?;
|
||||||
let source = get_consumed(input, remaining);
|
let source = get_consumed(input, remaining);
|
||||||
Ok((
|
Ok((
|
||||||
remaining,
|
remaining,
|
||||||
FootnoteDefinition {
|
FootnoteDefinition {
|
||||||
source: source.into(),
|
source: source.into(),
|
||||||
|
name: get_name(&affiliated_keywords),
|
||||||
label: lbl.into(),
|
label: lbl.into(),
|
||||||
children: children.into_iter().map(|(_, item)| item).collect(),
|
children: children.into_iter().map(|(_, item)| item).collect(),
|
||||||
},
|
},
|
||||||
@@ -119,6 +126,7 @@ fn footnote_definition_end<'b, 'g, 'r, 's>(
|
|||||||
|
|
||||||
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
|
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
|
||||||
pub(crate) fn detect_footnote_definition<'s>(input: OrgSource<'s>) -> Res<OrgSource<'s>, ()> {
|
pub(crate) fn detect_footnote_definition<'s>(input: OrgSource<'s>) -> Res<OrgSource<'s>, ()> {
|
||||||
|
let (input, _) = many0(affiliated_keyword)(input)?;
|
||||||
tuple((start_of_line, tag_no_case("[fn:"), label, tag("]")))(input)?;
|
tuple((start_of_line, tag_no_case("[fn:"), label, tag("]")))(input)?;
|
||||||
Ok((input, ()))
|
Ok((input, ()))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,8 +17,11 @@ use nom::multi::many_till;
|
|||||||
use nom::sequence::preceded;
|
use nom::sequence::preceded;
|
||||||
use nom::sequence::tuple;
|
use nom::sequence::tuple;
|
||||||
|
|
||||||
|
use super::keyword::affiliated_keyword;
|
||||||
use super::org_source::OrgSource;
|
use super::org_source::OrgSource;
|
||||||
|
use super::util::get_name;
|
||||||
use super::util::in_section;
|
use super::util::in_section;
|
||||||
|
use super::util::maybe_consume_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::ContextMatcher;
|
||||||
@@ -35,6 +38,7 @@ use crate::parser::util::get_consumed;
|
|||||||
use crate::parser::util::start_of_line;
|
use crate::parser::util::start_of_line;
|
||||||
use crate::types::CenterBlock;
|
use crate::types::CenterBlock;
|
||||||
use crate::types::Element;
|
use crate::types::Element;
|
||||||
|
use crate::types::Keyword;
|
||||||
use crate::types::Paragraph;
|
use crate::types::Paragraph;
|
||||||
use crate::types::QuoteBlock;
|
use crate::types::QuoteBlock;
|
||||||
use crate::types::SetSource;
|
use crate::types::SetSource;
|
||||||
@@ -45,6 +49,8 @@ pub(crate) fn greater_block<'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>, Element<'s>> {
|
) -> Res<OrgSource<'s>, Element<'s>> {
|
||||||
|
let pre_affiliated_keywords_input = input;
|
||||||
|
let (input, affiliated_keywords) = many0(affiliated_keyword)(input)?;
|
||||||
start_of_line(input)?;
|
start_of_line(input)?;
|
||||||
let (remaining, _leading_whitespace) = space0(input)?;
|
let (remaining, _leading_whitespace) = space0(input)?;
|
||||||
let (remaining, (_begin, name)) = tuple((
|
let (remaining, (_begin, name)) = tuple((
|
||||||
@@ -58,9 +64,24 @@ pub(crate) fn greater_block<'b, 'g, 'r, 's>(
|
|||||||
))(remaining)?;
|
))(remaining)?;
|
||||||
let name = Into::<&str>::into(name);
|
let name = Into::<&str>::into(name);
|
||||||
let (remaining, element) = match name.to_lowercase().as_str() {
|
let (remaining, element) = match name.to_lowercase().as_str() {
|
||||||
"center" => center_block(context, remaining, input)?,
|
"center" => center_block(
|
||||||
"quote" => quote_block(context, remaining, input)?,
|
context,
|
||||||
_ => special_block(name)(context, remaining, input)?,
|
remaining,
|
||||||
|
pre_affiliated_keywords_input,
|
||||||
|
&affiliated_keywords,
|
||||||
|
)?,
|
||||||
|
"quote" => quote_block(
|
||||||
|
context,
|
||||||
|
remaining,
|
||||||
|
pre_affiliated_keywords_input,
|
||||||
|
&affiliated_keywords,
|
||||||
|
)?,
|
||||||
|
_ => special_block(name)(
|
||||||
|
context,
|
||||||
|
remaining,
|
||||||
|
pre_affiliated_keywords_input,
|
||||||
|
&affiliated_keywords,
|
||||||
|
)?,
|
||||||
};
|
};
|
||||||
Ok((remaining, element))
|
Ok((remaining, element))
|
||||||
}
|
}
|
||||||
@@ -69,13 +90,23 @@ pub(crate) fn greater_block<'b, 'g, 'r, 's>(
|
|||||||
fn center_block<'b, 'g, 'r, 's>(
|
fn center_block<'b, 'g, 'r, 's>(
|
||||||
context: RefContext<'b, 'g, 'r, 's>,
|
context: RefContext<'b, 'g, 'r, 's>,
|
||||||
input: OrgSource<'s>,
|
input: OrgSource<'s>,
|
||||||
original_input: OrgSource<'s>,
|
pre_affiliated_keywords_input: OrgSource<'s>,
|
||||||
|
affiliated_keywords: &Vec<Keyword<'s>>,
|
||||||
) -> Res<OrgSource<'s>, Element<'s>> {
|
) -> Res<OrgSource<'s>, Element<'s>> {
|
||||||
let (remaining, (source, children)) =
|
let (remaining, (source, children)) = greater_block_body(
|
||||||
greater_block_body(context, input, original_input, "center", "center block")?;
|
context,
|
||||||
|
input,
|
||||||
|
pre_affiliated_keywords_input,
|
||||||
|
"center",
|
||||||
|
"center block",
|
||||||
|
)?;
|
||||||
Ok((
|
Ok((
|
||||||
remaining,
|
remaining,
|
||||||
Element::CenterBlock(CenterBlock { source, children }),
|
Element::CenterBlock(CenterBlock {
|
||||||
|
source,
|
||||||
|
name: get_name(&affiliated_keywords),
|
||||||
|
children,
|
||||||
|
}),
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -83,13 +114,23 @@ fn center_block<'b, 'g, 'r, 's>(
|
|||||||
fn quote_block<'b, 'g, 'r, 's>(
|
fn quote_block<'b, 'g, 'r, 's>(
|
||||||
context: RefContext<'b, 'g, 'r, 's>,
|
context: RefContext<'b, 'g, 'r, 's>,
|
||||||
input: OrgSource<'s>,
|
input: OrgSource<'s>,
|
||||||
original_input: OrgSource<'s>,
|
pre_affiliated_keywords_input: OrgSource<'s>,
|
||||||
|
affiliated_keywords: &Vec<Keyword<'s>>,
|
||||||
) -> Res<OrgSource<'s>, Element<'s>> {
|
) -> Res<OrgSource<'s>, Element<'s>> {
|
||||||
let (remaining, (source, children)) =
|
let (remaining, (source, children)) = greater_block_body(
|
||||||
greater_block_body(context, input, original_input, "quote", "quote block")?;
|
context,
|
||||||
|
input,
|
||||||
|
pre_affiliated_keywords_input,
|
||||||
|
"quote",
|
||||||
|
"quote block",
|
||||||
|
)?;
|
||||||
Ok((
|
Ok((
|
||||||
remaining,
|
remaining,
|
||||||
Element::QuoteBlock(QuoteBlock { source, children }),
|
Element::QuoteBlock(QuoteBlock {
|
||||||
|
source,
|
||||||
|
name: get_name(&affiliated_keywords),
|
||||||
|
children,
|
||||||
|
}),
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -99,11 +140,19 @@ fn special_block<'s>(
|
|||||||
RefContext<'b, 'g, 'r, 's>,
|
RefContext<'b, 'g, 'r, 's>,
|
||||||
OrgSource<'s>,
|
OrgSource<'s>,
|
||||||
OrgSource<'s>,
|
OrgSource<'s>,
|
||||||
|
&Vec<Keyword<'s>>,
|
||||||
) -> Res<OrgSource<'s>, Element<'s>>
|
) -> Res<OrgSource<'s>, Element<'s>>
|
||||||
+ 's {
|
+ 's {
|
||||||
let context_name = format!("special block {}", name);
|
let context_name = format!("special block {}", name);
|
||||||
move |context, input, original_input| {
|
move |context, input, pre_affiliated_keywords_input, affiliated_keywords| {
|
||||||
_special_block(context, input, original_input, name, context_name.as_str())
|
_special_block(
|
||||||
|
context,
|
||||||
|
input,
|
||||||
|
pre_affiliated_keywords_input,
|
||||||
|
name,
|
||||||
|
context_name.as_str(),
|
||||||
|
affiliated_keywords,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -111,19 +160,26 @@ fn special_block<'s>(
|
|||||||
fn _special_block<'c, 'b, 'g, 'r, 's>(
|
fn _special_block<'c, 'b, 'g, 'r, 's>(
|
||||||
context: RefContext<'b, 'g, 'r, 's>,
|
context: RefContext<'b, 'g, 'r, 's>,
|
||||||
input: OrgSource<'s>,
|
input: OrgSource<'s>,
|
||||||
original_input: OrgSource<'s>,
|
pre_affiliated_keywords_input: OrgSource<'s>,
|
||||||
name: &'s str,
|
name: &'s str,
|
||||||
context_name: &'c str,
|
context_name: &'c str,
|
||||||
|
affiliated_keywords: &Vec<Keyword<'s>>,
|
||||||
) -> Res<OrgSource<'s>, Element<'s>> {
|
) -> Res<OrgSource<'s>, Element<'s>> {
|
||||||
let (remaining, parameters) = opt(tuple((space1, parameters)))(input)?;
|
let (remaining, parameters) = opt(tuple((space1, parameters)))(input)?;
|
||||||
let (remaining, (source, children)) =
|
let (remaining, (source, children)) = greater_block_body(
|
||||||
greater_block_body(context, remaining, original_input, name, context_name)?;
|
context,
|
||||||
|
remaining,
|
||||||
|
pre_affiliated_keywords_input,
|
||||||
|
name,
|
||||||
|
context_name,
|
||||||
|
)?;
|
||||||
Ok((
|
Ok((
|
||||||
remaining,
|
remaining,
|
||||||
Element::SpecialBlock(SpecialBlock {
|
Element::SpecialBlock(SpecialBlock {
|
||||||
source,
|
source,
|
||||||
|
name: get_name(&affiliated_keywords),
|
||||||
children,
|
children,
|
||||||
name,
|
block_type: name,
|
||||||
parameters: parameters.map(|(_, parameters)| Into::<&str>::into(parameters)),
|
parameters: parameters.map(|(_, parameters)| Into::<&str>::into(parameters)),
|
||||||
}),
|
}),
|
||||||
))
|
))
|
||||||
@@ -133,7 +189,7 @@ fn _special_block<'c, 'b, 'g, 'r, 's>(
|
|||||||
fn greater_block_body<'c, 'b, 'g, 'r, 's>(
|
fn greater_block_body<'c, 'b, 'g, 'r, 's>(
|
||||||
context: RefContext<'b, 'g, 'r, 's>,
|
context: RefContext<'b, 'g, 'r, 's>,
|
||||||
input: OrgSource<'s>,
|
input: OrgSource<'s>,
|
||||||
original_input: OrgSource<'s>,
|
pre_affiliated_keywords_input: OrgSource<'s>,
|
||||||
name: &'c str,
|
name: &'c str,
|
||||||
context_name: &'c str,
|
context_name: &'c str,
|
||||||
) -> Res<OrgSource<'s>, (&'s str, Vec<Element<'s>>)> {
|
) -> Res<OrgSource<'s>, (&'s str, Vec<Element<'s>>)> {
|
||||||
@@ -178,7 +234,9 @@ fn greater_block_body<'c, 'b, 'g, 'r, 's>(
|
|||||||
|
|
||||||
// Not checking if parent exit matcher is causing exit because the greater_block_end matcher asserts we matched a full greater block
|
// Not checking if parent exit matcher is causing exit because the greater_block_end matcher asserts we matched a full greater block
|
||||||
|
|
||||||
let source = get_consumed(original_input, remaining);
|
let (remaining, _trailing_ws) =
|
||||||
|
maybe_consume_trailing_whitespace_if_not_exiting(context, remaining)?;
|
||||||
|
let source = get_consumed(pre_affiliated_keywords_input, remaining);
|
||||||
Ok((remaining, (Into::<&str>::into(source), children)))
|
Ok((remaining, (Into::<&str>::into(source), children)))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -5,10 +5,15 @@ use nom::character::complete::space0;
|
|||||||
use nom::combinator::eof;
|
use nom::combinator::eof;
|
||||||
use nom::combinator::recognize;
|
use nom::combinator::recognize;
|
||||||
use nom::combinator::verify;
|
use nom::combinator::verify;
|
||||||
|
use nom::multi::many0;
|
||||||
use nom::multi::many1_count;
|
use nom::multi::many1_count;
|
||||||
use nom::sequence::tuple;
|
use nom::sequence::tuple;
|
||||||
|
|
||||||
|
use super::keyword::affiliated_keyword;
|
||||||
use super::org_source::OrgSource;
|
use super::org_source::OrgSource;
|
||||||
|
use super::util::get_consumed;
|
||||||
|
use super::util::get_name;
|
||||||
|
use super::util::maybe_consume_trailing_whitespace_if_not_exiting;
|
||||||
use crate::context::RefContext;
|
use crate::context::RefContext;
|
||||||
use crate::error::Res;
|
use crate::error::Res;
|
||||||
use crate::parser::util::start_of_line;
|
use crate::parser::util::start_of_line;
|
||||||
@@ -16,20 +21,25 @@ use crate::types::HorizontalRule;
|
|||||||
|
|
||||||
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
|
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
|
||||||
pub(crate) fn horizontal_rule<'b, 'g, 'r, 's>(
|
pub(crate) fn horizontal_rule<'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>, HorizontalRule<'s>> {
|
) -> Res<OrgSource<'s>, HorizontalRule<'s>> {
|
||||||
start_of_line(input)?;
|
let (remaining, affiliated_keywords) = many0(affiliated_keyword)(input)?;
|
||||||
let (remaining, rule) = recognize(tuple((
|
start_of_line(remaining)?;
|
||||||
|
let (remaining, _rule) = recognize(tuple((
|
||||||
space0,
|
space0,
|
||||||
verify(many1_count(tag("-")), |dashes| *dashes >= 5),
|
verify(many1_count(tag("-")), |dashes| *dashes >= 5),
|
||||||
space0,
|
space0,
|
||||||
alt((line_ending, eof)),
|
alt((line_ending, eof)),
|
||||||
)))(input)?;
|
)))(remaining)?;
|
||||||
|
let (remaining, _trailing_ws) =
|
||||||
|
maybe_consume_trailing_whitespace_if_not_exiting(context, remaining)?;
|
||||||
|
let source = get_consumed(input, remaining);
|
||||||
Ok((
|
Ok((
|
||||||
remaining,
|
remaining,
|
||||||
HorizontalRule {
|
HorizontalRule {
|
||||||
source: rule.into(),
|
source: source.into(),
|
||||||
|
name: get_name(&affiliated_keywords),
|
||||||
},
|
},
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,8 +2,17 @@ use nom::branch::alt;
|
|||||||
use nom::bytes::complete::is_not;
|
use nom::bytes::complete::is_not;
|
||||||
use nom::bytes::complete::tag_no_case;
|
use nom::bytes::complete::tag_no_case;
|
||||||
use nom::bytes::complete::take_until;
|
use nom::bytes::complete::take_until;
|
||||||
|
use nom::character::complete::anychar;
|
||||||
|
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::map;
|
||||||
|
use nom::combinator::peek;
|
||||||
|
use nom::combinator::recognize;
|
||||||
|
use nom::multi::many_till;
|
||||||
use nom::multi::separated_list0;
|
use nom::multi::separated_list0;
|
||||||
|
use nom::sequence::tuple;
|
||||||
|
|
||||||
use super::keyword::filtered_keyword;
|
use super::keyword::filtered_keyword;
|
||||||
use super::keyword_todo::todo_keywords;
|
use super::keyword_todo::todo_keywords;
|
||||||
@@ -75,6 +84,7 @@ fn in_buffer_settings_key<'s>(input: OrgSource<'s>) -> Res<OrgSource<'s>, OrgSou
|
|||||||
))(input)
|
))(input)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
|
||||||
pub(crate) fn apply_in_buffer_settings<'g, 's, 'sf>(
|
pub(crate) fn apply_in_buffer_settings<'g, 's, 'sf>(
|
||||||
keywords: Vec<Keyword<'sf>>,
|
keywords: Vec<Keyword<'sf>>,
|
||||||
original_settings: &'g GlobalSettings<'g, 's>,
|
original_settings: &'g GlobalSettings<'g, 's>,
|
||||||
@@ -113,10 +123,22 @@ pub(crate) fn apply_in_buffer_settings<'g, 's, 'sf>(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Link templates
|
||||||
|
for kw in keywords
|
||||||
|
.iter()
|
||||||
|
.filter(|kw| kw.key.eq_ignore_ascii_case("link"))
|
||||||
|
{
|
||||||
|
let (_, (link_key, link_value)) = link_template(kw.value).map_err(|e| e.to_string())?;
|
||||||
|
new_settings
|
||||||
|
.link_templates
|
||||||
|
.insert(link_key.to_owned(), link_value.to_owned());
|
||||||
|
}
|
||||||
|
|
||||||
Ok(new_settings)
|
Ok(new_settings)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Apply in-buffer settings that do not impact parsing and therefore can be applied after parsing.
|
/// Apply in-buffer settings that do not impact parsing and therefore can be applied after parsing.
|
||||||
|
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
|
||||||
pub(crate) fn apply_post_parse_in_buffer_settings<'g, 's, 'sf>(
|
pub(crate) fn apply_post_parse_in_buffer_settings<'g, 's, 'sf>(
|
||||||
document: &mut Document<'s>,
|
document: &mut Document<'s>,
|
||||||
) -> Result<(), &'static str> {
|
) -> Result<(), &'static str> {
|
||||||
@@ -135,6 +157,30 @@ pub(crate) fn apply_post_parse_in_buffer_settings<'g, 's, 'sf>(
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
|
||||||
|
fn link_template<'s>(input: &'s str) -> Res<&'s str, (&'s str, &'s str)> {
|
||||||
|
let (remaining, key) = map(
|
||||||
|
tuple((
|
||||||
|
space0,
|
||||||
|
recognize(many_till(anychar, peek(alt((space1, line_ending, eof))))),
|
||||||
|
)),
|
||||||
|
|(_, key)| key,
|
||||||
|
)(input)?;
|
||||||
|
|
||||||
|
let (remaining, replacement) = map(
|
||||||
|
tuple((
|
||||||
|
space1,
|
||||||
|
recognize(many_till(
|
||||||
|
anychar,
|
||||||
|
peek(tuple((space0, alt((line_ending, eof))))),
|
||||||
|
)),
|
||||||
|
)),
|
||||||
|
|(_, replacement)| replacement,
|
||||||
|
)(remaining)?;
|
||||||
|
|
||||||
|
Ok((remaining, (key, replacement)))
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|||||||
@@ -13,18 +13,21 @@ 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::many_till;
|
use nom::multi::many_till;
|
||||||
use nom::sequence::tuple;
|
use nom::sequence::tuple;
|
||||||
|
|
||||||
use super::org_source::BracketDepth;
|
use super::org_source::BracketDepth;
|
||||||
use super::org_source::OrgSource;
|
use super::org_source::OrgSource;
|
||||||
|
use super::util::get_consumed;
|
||||||
|
use super::util::get_name;
|
||||||
|
use super::util::maybe_consume_trailing_whitespace_if_not_exiting;
|
||||||
use crate::context::Matcher;
|
use crate::context::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;
|
||||||
use crate::error::Res;
|
use crate::error::Res;
|
||||||
use crate::parser::util::start_of_line;
|
use crate::parser::util::start_of_line;
|
||||||
use crate::types::BabelCall;
|
|
||||||
use crate::types::Keyword;
|
use crate::types::Keyword;
|
||||||
|
|
||||||
const ORG_ELEMENT_AFFILIATED_KEYWORDS: [&'static str; 13] = [
|
const ORG_ELEMENT_AFFILIATED_KEYWORDS: [&'static str; 13] = [
|
||||||
@@ -61,6 +64,7 @@ fn _filtered_keyword<'s, F: Matcher>(
|
|||||||
remaining,
|
remaining,
|
||||||
Keyword {
|
Keyword {
|
||||||
source: consumed_input.into(),
|
source: consumed_input.into(),
|
||||||
|
name: None, // To be populated by the caller if this keyword is in a context to support affiliated keywords.
|
||||||
key: parsed_key.into(),
|
key: parsed_key.into(),
|
||||||
value: "".into(),
|
value: "".into(),
|
||||||
},
|
},
|
||||||
@@ -78,6 +82,7 @@ fn _filtered_keyword<'s, F: Matcher>(
|
|||||||
remaining,
|
remaining,
|
||||||
Keyword {
|
Keyword {
|
||||||
source: consumed_input.into(),
|
source: consumed_input.into(),
|
||||||
|
name: None, // To be populated by the caller if this keyword is in a context to support affiliated keywords.
|
||||||
key: parsed_key.into(),
|
key: parsed_key.into(),
|
||||||
value: parsed_value.into(),
|
value: parsed_value.into(),
|
||||||
},
|
},
|
||||||
@@ -86,41 +91,37 @@ fn _filtered_keyword<'s, F: Matcher>(
|
|||||||
|
|
||||||
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
|
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
|
||||||
pub(crate) fn keyword<'b, 'g, 'r, 's>(
|
pub(crate) fn 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(regular_keyword_key)(input)
|
let (remaining, affiliated_keywords) = many0(affiliated_keyword)(input)?;
|
||||||
|
let (remaining, mut kw) = filtered_keyword(regular_keyword_key)(remaining)?;
|
||||||
|
let (remaining, _trailing_ws) =
|
||||||
|
maybe_consume_trailing_whitespace_if_not_exiting(context, remaining)?;
|
||||||
|
let source = get_consumed(input, remaining);
|
||||||
|
kw.name = get_name(&affiliated_keywords);
|
||||||
|
kw.source = Into::<&str>::into(source);
|
||||||
|
Ok((remaining, kw))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
|
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
|
||||||
pub(crate) fn affiliated_keyword<'b, 'g, 'r, 's>(
|
pub(crate) fn affiliated_keyword_as_regular_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>> {
|
||||||
|
let (remaining, mut kw) = affiliated_keyword(input)?;
|
||||||
|
let (remaining, _trailing_ws) =
|
||||||
|
maybe_consume_trailing_whitespace_if_not_exiting(context, remaining)?;
|
||||||
|
let source = get_consumed(input, remaining);
|
||||||
|
kw.source = Into::<&str>::into(source);
|
||||||
|
Ok((remaining, kw))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
|
||||||
|
pub(crate) fn affiliated_keyword<'s>(input: OrgSource<'s>) -> Res<OrgSource<'s>, Keyword<'s>> {
|
||||||
filtered_keyword(affiliated_key)(input)
|
filtered_keyword(affiliated_key)(input)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
|
|
||||||
pub(crate) fn babel_call_keyword<'b, 'g, 'r, 's>(
|
|
||||||
_context: RefContext<'b, 'g, 'r, 's>,
|
|
||||||
input: OrgSource<'s>,
|
|
||||||
) -> Res<OrgSource<'s>, BabelCall<'s>> {
|
|
||||||
let (remaining, kw) = filtered_keyword(babel_call_key)(input)?;
|
|
||||||
Ok((
|
|
||||||
remaining,
|
|
||||||
BabelCall {
|
|
||||||
source: kw.source,
|
|
||||||
key: kw.key,
|
|
||||||
value: kw.value,
|
|
||||||
},
|
|
||||||
))
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
|
|
||||||
fn babel_call_key<'s>(input: OrgSource<'s>) -> Res<OrgSource<'s>, OrgSource<'s>> {
|
|
||||||
tag_no_case("call")(input)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
|
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
|
||||||
pub(crate) fn table_formula_keyword<'b, 'g, 'r, 's>(
|
pub(crate) fn table_formula_keyword<'b, 'g, 'r, 's>(
|
||||||
_context: RefContext<'b, 'g, 'r, 's>,
|
_context: RefContext<'b, 'g, 'r, 's>,
|
||||||
|
|||||||
@@ -8,11 +8,15 @@ use nom::character::complete::space0;
|
|||||||
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::multi::many0;
|
||||||
use nom::multi::many_till;
|
use nom::multi::many_till;
|
||||||
use nom::sequence::tuple;
|
use nom::sequence::tuple;
|
||||||
|
|
||||||
|
use super::keyword::affiliated_keyword;
|
||||||
use super::org_source::OrgSource;
|
use super::org_source::OrgSource;
|
||||||
use super::util::get_consumed;
|
use super::util::get_consumed;
|
||||||
|
use super::util::get_name;
|
||||||
|
use super::util::maybe_consume_trailing_whitespace_if_not_exiting;
|
||||||
use crate::context::parser_with_context;
|
use crate::context::parser_with_context;
|
||||||
use crate::context::ContextElement;
|
use crate::context::ContextElement;
|
||||||
use crate::context::ContextMatcher;
|
use crate::context::ContextMatcher;
|
||||||
@@ -29,8 +33,10 @@ pub(crate) fn latex_environment<'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>, LatexEnvironment<'s>> {
|
) -> Res<OrgSource<'s>, LatexEnvironment<'s>> {
|
||||||
start_of_line(input)?;
|
let (remaining, affiliated_keywords) = many0(affiliated_keyword)(input)?;
|
||||||
let (remaining, _leading_whitespace) = space0(input)?;
|
let value_start = remaining;
|
||||||
|
start_of_line(remaining)?;
|
||||||
|
let (remaining, _leading_whitespace) = space0(remaining)?;
|
||||||
let (remaining, (_opening, name, _open_close_brace, _ws, _line_ending)) = tuple((
|
let (remaining, (_opening, name, _open_close_brace, _ws, _line_ending)) = tuple((
|
||||||
tag_no_case(r#"\begin{"#),
|
tag_no_case(r#"\begin{"#),
|
||||||
name,
|
name,
|
||||||
@@ -48,12 +54,18 @@ pub(crate) fn latex_environment<'b, 'g, 'r, 's>(
|
|||||||
|
|
||||||
let (remaining, _contents) = contents(&latex_environment_end_specialized)(context, remaining)?;
|
let (remaining, _contents) = contents(&latex_environment_end_specialized)(context, remaining)?;
|
||||||
let (remaining, _end) = latex_environment_end_specialized(&parser_context, remaining)?;
|
let (remaining, _end) = latex_environment_end_specialized(&parser_context, remaining)?;
|
||||||
|
let value_end = remaining;
|
||||||
|
|
||||||
|
let (remaining, _trailing_ws) =
|
||||||
|
maybe_consume_trailing_whitespace_if_not_exiting(context, remaining)?;
|
||||||
let source = get_consumed(input, remaining);
|
let source = get_consumed(input, remaining);
|
||||||
|
let value = get_consumed(value_start, value_end);
|
||||||
Ok((
|
Ok((
|
||||||
remaining,
|
remaining,
|
||||||
LatexEnvironment {
|
LatexEnvironment {
|
||||||
source: source.into(),
|
source: source.into(),
|
||||||
|
name: get_name(&affiliated_keywords),
|
||||||
|
value: value.into(),
|
||||||
},
|
},
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,24 +1,34 @@
|
|||||||
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::anychar;
|
||||||
use nom::character::complete::line_ending;
|
use nom::character::complete::line_ending;
|
||||||
use nom::character::complete::space0;
|
use nom::character::complete::space0;
|
||||||
use nom::character::complete::space1;
|
use nom::character::complete::space1;
|
||||||
use nom::combinator::consumed;
|
use nom::combinator::consumed;
|
||||||
use nom::combinator::eof;
|
use nom::combinator::eof;
|
||||||
|
use nom::combinator::map;
|
||||||
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::many_till;
|
use nom::multi::many_till;
|
||||||
use nom::sequence::tuple;
|
use nom::sequence::tuple;
|
||||||
|
|
||||||
|
use super::keyword::affiliated_keyword;
|
||||||
use super::org_source::OrgSource;
|
use super::org_source::OrgSource;
|
||||||
|
use super::util::get_name;
|
||||||
|
use super::util::maybe_consume_trailing_whitespace_if_not_exiting;
|
||||||
use crate::context::parser_with_context;
|
use crate::context::parser_with_context;
|
||||||
use crate::context::ContextElement;
|
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::RefContext;
|
use crate::context::RefContext;
|
||||||
|
use crate::error::CustomError;
|
||||||
|
use crate::error::MyError;
|
||||||
use crate::error::Res;
|
use crate::error::Res;
|
||||||
use crate::parser::object_parser::standard_set_object;
|
use crate::parser::object_parser::standard_set_object;
|
||||||
use crate::parser::util::blank_line;
|
use crate::parser::util::blank_line;
|
||||||
@@ -26,12 +36,16 @@ use crate::parser::util::exit_matcher_parser;
|
|||||||
use crate::parser::util::get_consumed;
|
use crate::parser::util::get_consumed;
|
||||||
use crate::parser::util::start_of_line;
|
use crate::parser::util::start_of_line;
|
||||||
use crate::parser::util::text_until_exit;
|
use crate::parser::util::text_until_exit;
|
||||||
|
use crate::types::CharOffsetInLine;
|
||||||
use crate::types::CommentBlock;
|
use crate::types::CommentBlock;
|
||||||
use crate::types::ExampleBlock;
|
use crate::types::ExampleBlock;
|
||||||
use crate::types::ExportBlock;
|
use crate::types::ExportBlock;
|
||||||
|
use crate::types::LineNumber;
|
||||||
use crate::types::Object;
|
use crate::types::Object;
|
||||||
use crate::types::PlainText;
|
use crate::types::PlainText;
|
||||||
|
use crate::types::RetainLabels;
|
||||||
use crate::types::SrcBlock;
|
use crate::types::SrcBlock;
|
||||||
|
use crate::types::SwitchNumberLines;
|
||||||
use crate::types::VerseBlock;
|
use crate::types::VerseBlock;
|
||||||
|
|
||||||
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
|
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
|
||||||
@@ -39,7 +53,8 @@ pub(crate) fn verse_block<'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>, VerseBlock<'s>> {
|
) -> Res<OrgSource<'s>, VerseBlock<'s>> {
|
||||||
let (remaining, name) = lesser_block_begin("verse")(context, input)?;
|
let (remaining, affiliated_keywords) = many0(affiliated_keyword)(input)?;
|
||||||
|
let (remaining, _) = lesser_block_begin("verse")(context, remaining)?;
|
||||||
let (remaining, parameters) = opt(tuple((space1, data)))(remaining)?;
|
let (remaining, parameters) = opt(tuple((space1, data)))(remaining)?;
|
||||||
let (remaining, _nl) = recognize(tuple((space0, line_ending)))(remaining)?;
|
let (remaining, _nl) = recognize(tuple((space0, line_ending)))(remaining)?;
|
||||||
let lesser_block_end_specialized = lesser_block_end("verse");
|
let lesser_block_end_specialized = lesser_block_end("verse");
|
||||||
@@ -77,12 +92,14 @@ pub(crate) fn verse_block<'b, 'g, 'r, 's>(
|
|||||||
};
|
};
|
||||||
let (remaining, _end) = lesser_block_end_specialized(&parser_context, remaining)?;
|
let (remaining, _end) = lesser_block_end_specialized(&parser_context, remaining)?;
|
||||||
|
|
||||||
|
let (remaining, _trailing_ws) =
|
||||||
|
maybe_consume_trailing_whitespace_if_not_exiting(context, remaining)?;
|
||||||
let source = get_consumed(input, remaining);
|
let source = get_consumed(input, remaining);
|
||||||
Ok((
|
Ok((
|
||||||
remaining,
|
remaining,
|
||||||
VerseBlock {
|
VerseBlock {
|
||||||
source: source.into(),
|
source: source.into(),
|
||||||
name: name.into(),
|
name: get_name(&affiliated_keywords),
|
||||||
data: parameters.map(|parameters| Into::<&str>::into(parameters)),
|
data: parameters.map(|parameters| Into::<&str>::into(parameters)),
|
||||||
children,
|
children,
|
||||||
},
|
},
|
||||||
@@ -94,8 +111,9 @@ pub(crate) fn comment_block<'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>, CommentBlock<'s>> {
|
) -> Res<OrgSource<'s>, CommentBlock<'s>> {
|
||||||
let (remaining, name) = lesser_block_begin("comment")(context, input)?;
|
let (remaining, affiliated_keywords) = many0(affiliated_keyword)(input)?;
|
||||||
let (remaining, parameters) = opt(tuple((space1, data)))(remaining)?;
|
let (remaining, _) = lesser_block_begin("comment")(context, remaining)?;
|
||||||
|
let (remaining, _parameters) = opt(tuple((space1, data)))(remaining)?;
|
||||||
let (remaining, _nl) = recognize(tuple((space0, line_ending)))(remaining)?;
|
let (remaining, _nl) = recognize(tuple((space0, line_ending)))(remaining)?;
|
||||||
let lesser_block_end_specialized = lesser_block_end("comment");
|
let lesser_block_end_specialized = lesser_block_end("comment");
|
||||||
let contexts = [
|
let contexts = [
|
||||||
@@ -109,21 +127,18 @@ pub(crate) fn comment_block<'b, 'g, 'r, 's>(
|
|||||||
let parser_context = context.with_additional_node(&contexts[0]);
|
let parser_context = context.with_additional_node(&contexts[0]);
|
||||||
let parser_context = parser_context.with_additional_node(&contexts[1]);
|
let parser_context = parser_context.with_additional_node(&contexts[1]);
|
||||||
let parser_context = parser_context.with_additional_node(&contexts[2]);
|
let parser_context = parser_context.with_additional_node(&contexts[2]);
|
||||||
let parameters = match parameters {
|
|
||||||
Some((_ws, parameters)) => Some(parameters),
|
|
||||||
None => None,
|
|
||||||
};
|
|
||||||
|
|
||||||
let (remaining, contents) = parser_with_context!(text_until_exit)(&parser_context)(remaining)?;
|
let (remaining, contents) = parser_with_context!(text_until_exit)(&parser_context)(remaining)?;
|
||||||
let (remaining, _end) = lesser_block_end_specialized(&parser_context, remaining)?;
|
let (remaining, _end) = lesser_block_end_specialized(&parser_context, remaining)?;
|
||||||
|
|
||||||
|
let (remaining, _trailing_ws) =
|
||||||
|
maybe_consume_trailing_whitespace_if_not_exiting(context, remaining)?;
|
||||||
let source = get_consumed(input, remaining);
|
let source = get_consumed(input, remaining);
|
||||||
Ok((
|
Ok((
|
||||||
remaining,
|
remaining,
|
||||||
CommentBlock {
|
CommentBlock {
|
||||||
source: source.into(),
|
source: source.into(),
|
||||||
name: name.into(),
|
name: get_name(&affiliated_keywords),
|
||||||
data: parameters.map(|parameters| Into::<&str>::into(parameters)),
|
|
||||||
contents: contents.into(),
|
contents: contents.into(),
|
||||||
},
|
},
|
||||||
))
|
))
|
||||||
@@ -134,8 +149,9 @@ pub(crate) fn example_block<'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>, ExampleBlock<'s>> {
|
) -> Res<OrgSource<'s>, ExampleBlock<'s>> {
|
||||||
let (remaining, _name) = lesser_block_begin("example")(context, input)?;
|
let (remaining, affiliated_keywords) = many0(affiliated_keyword)(input)?;
|
||||||
let (remaining, parameters) = opt(tuple((space1, data)))(remaining)?;
|
let (remaining, _) = lesser_block_begin("example")(context, remaining)?;
|
||||||
|
let (remaining, parameters) = opt(tuple((space1, example_switches)))(remaining)?;
|
||||||
let (remaining, _nl) = recognize(tuple((space0, line_ending)))(remaining)?;
|
let (remaining, _nl) = recognize(tuple((space0, line_ending)))(remaining)?;
|
||||||
let lesser_block_end_specialized = lesser_block_end("example");
|
let lesser_block_end_specialized = lesser_block_end("example");
|
||||||
let contexts = [
|
let contexts = [
|
||||||
@@ -149,22 +165,44 @@ pub(crate) fn example_block<'b, 'g, 'r, 's>(
|
|||||||
let parser_context = context.with_additional_node(&contexts[0]);
|
let parser_context = context.with_additional_node(&contexts[0]);
|
||||||
let parser_context = parser_context.with_additional_node(&contexts[1]);
|
let parser_context = parser_context.with_additional_node(&contexts[1]);
|
||||||
let parser_context = parser_context.with_additional_node(&contexts[2]);
|
let parser_context = parser_context.with_additional_node(&contexts[2]);
|
||||||
let parameters = match parameters {
|
let parameters = parameters.map(|(_, parameters)| parameters);
|
||||||
Some((_ws, parameters)) => Some(parameters),
|
|
||||||
None => None,
|
|
||||||
};
|
|
||||||
|
|
||||||
let (remaining, contents) = parser_with_context!(text_until_exit)(&parser_context)(remaining)?;
|
let (remaining, contents) = content(&parser_context, remaining)?;
|
||||||
let (remaining, _end) = lesser_block_end_specialized(&parser_context, remaining)?;
|
let (remaining, _end) = lesser_block_end_specialized(&parser_context, remaining)?;
|
||||||
|
|
||||||
|
let (remaining, _trailing_ws) =
|
||||||
|
maybe_consume_trailing_whitespace_if_not_exiting(context, remaining)?;
|
||||||
let source = get_consumed(input, remaining);
|
let source = get_consumed(input, remaining);
|
||||||
|
let (switches, number_lines, preserve_indent, retain_labels, use_labels, label_format) = {
|
||||||
|
if let Some(parameters) = parameters {
|
||||||
|
(
|
||||||
|
if parameters.source.len() == 0 {
|
||||||
|
None
|
||||||
|
} else {
|
||||||
|
Some(parameters.source)
|
||||||
|
},
|
||||||
|
parameters.number_lines,
|
||||||
|
parameters.preserve_indent,
|
||||||
|
parameters.retain_labels,
|
||||||
|
parameters.use_labels,
|
||||||
|
parameters.label_format,
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
(None, None, None, RetainLabels::Yes, true, None)
|
||||||
|
}
|
||||||
|
};
|
||||||
Ok((
|
Ok((
|
||||||
remaining,
|
remaining,
|
||||||
ExampleBlock {
|
ExampleBlock {
|
||||||
source: source.into(),
|
source: source.into(),
|
||||||
name: source.into(),
|
name: get_name(&affiliated_keywords),
|
||||||
data: parameters.map(|parameters| Into::<&str>::into(parameters)),
|
switches,
|
||||||
contents: contents.into(),
|
number_lines,
|
||||||
|
preserve_indent,
|
||||||
|
retain_labels,
|
||||||
|
use_labels,
|
||||||
|
label_format,
|
||||||
|
contents,
|
||||||
},
|
},
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
@@ -174,9 +212,15 @@ pub(crate) fn export_block<'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>, ExportBlock<'s>> {
|
) -> Res<OrgSource<'s>, ExportBlock<'s>> {
|
||||||
let (remaining, name) = lesser_block_begin("export")(context, input)?;
|
let (remaining, affiliated_keywords) = many0(affiliated_keyword)(input)?;
|
||||||
|
let (remaining, _) = lesser_block_begin("export")(context, remaining)?;
|
||||||
// https://orgmode.org/worg/org-syntax.html#Blocks claims that export blocks must have a single word for data but testing shows no data and multi-word data still parses as an export block.
|
// https://orgmode.org/worg/org-syntax.html#Blocks claims that export blocks must have a single word for data but testing shows no data and multi-word data still parses as an export block.
|
||||||
let (remaining, parameters) = opt(tuple((space1, data)))(remaining)?;
|
let (remaining, export_type) = opt(map(
|
||||||
|
tuple((space1, switch_word, peek(tuple((space0, line_ending))))),
|
||||||
|
|(_, export_type, _)| export_type,
|
||||||
|
))(remaining)?;
|
||||||
|
let (remaining, parameters) =
|
||||||
|
opt(map(tuple((space1, data)), |(_, parameters)| parameters))(remaining)?;
|
||||||
let (remaining, _nl) = recognize(tuple((space0, line_ending)))(remaining)?;
|
let (remaining, _nl) = recognize(tuple((space0, line_ending)))(remaining)?;
|
||||||
let lesser_block_end_specialized = lesser_block_end("export");
|
let lesser_block_end_specialized = lesser_block_end("export");
|
||||||
let contexts = [
|
let contexts = [
|
||||||
@@ -190,22 +234,21 @@ pub(crate) fn export_block<'b, 'g, 'r, 's>(
|
|||||||
let parser_context = context.with_additional_node(&contexts[0]);
|
let parser_context = context.with_additional_node(&contexts[0]);
|
||||||
let parser_context = parser_context.with_additional_node(&contexts[1]);
|
let parser_context = parser_context.with_additional_node(&contexts[1]);
|
||||||
let parser_context = parser_context.with_additional_node(&contexts[2]);
|
let parser_context = parser_context.with_additional_node(&contexts[2]);
|
||||||
let parameters = match parameters {
|
|
||||||
Some((_ws, parameters)) => Some(parameters),
|
|
||||||
None => None,
|
|
||||||
};
|
|
||||||
|
|
||||||
let (remaining, contents) = parser_with_context!(text_until_exit)(&parser_context)(remaining)?;
|
let (remaining, contents) = content(&parser_context, remaining)?;
|
||||||
let (remaining, _end) = lesser_block_end_specialized(&parser_context, remaining)?;
|
let (remaining, _end) = lesser_block_end_specialized(&parser_context, remaining)?;
|
||||||
|
|
||||||
|
let (remaining, _trailing_ws) =
|
||||||
|
maybe_consume_trailing_whitespace_if_not_exiting(context, remaining)?;
|
||||||
let source = get_consumed(input, remaining);
|
let source = get_consumed(input, remaining);
|
||||||
Ok((
|
Ok((
|
||||||
remaining,
|
remaining,
|
||||||
ExportBlock {
|
ExportBlock {
|
||||||
source: source.into(),
|
source: source.into(),
|
||||||
name: name.into(),
|
name: get_name(&affiliated_keywords),
|
||||||
|
export_type: export_type.map(Into::<&str>::into),
|
||||||
data: parameters.map(|parameters| Into::<&str>::into(parameters)),
|
data: parameters.map(|parameters| Into::<&str>::into(parameters)),
|
||||||
contents: contents.into(),
|
contents,
|
||||||
},
|
},
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
@@ -215,9 +258,15 @@ pub(crate) fn src_block<'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>, SrcBlock<'s>> {
|
) -> Res<OrgSource<'s>, SrcBlock<'s>> {
|
||||||
let (remaining, name) = lesser_block_begin("src")(context, input)?;
|
let (remaining, affiliated_keywords) = many0(affiliated_keyword)(input)?;
|
||||||
|
let (remaining, _) = lesser_block_begin("src")(context, remaining)?;
|
||||||
// https://orgmode.org/worg/org-syntax.html#Blocks claims that data is mandatory and must follow the LANGUAGE SWITCHES ARGUMENTS pattern but testing has shown that no data and incorrect data here will still parse to a src block.
|
// https://orgmode.org/worg/org-syntax.html#Blocks claims that data is mandatory and must follow the LANGUAGE SWITCHES ARGUMENTS pattern but testing has shown that no data and incorrect data here will still parse to a src block.
|
||||||
let (remaining, parameters) = opt(tuple((space1, data)))(remaining)?;
|
let (remaining, language) =
|
||||||
|
opt(map(tuple((space1, switch_word)), |(_, language)| language))(remaining)?;
|
||||||
|
let (remaining, switches) = opt(src_switches)(remaining)?;
|
||||||
|
let (remaining, parameters) = opt(map(tuple((space1, src_parameters)), |(_, parameters)| {
|
||||||
|
parameters
|
||||||
|
}))(remaining)?;
|
||||||
let (remaining, _nl) = recognize(tuple((space0, line_ending)))(remaining)?;
|
let (remaining, _nl) = recognize(tuple((space0, line_ending)))(remaining)?;
|
||||||
let lesser_block_end_specialized = lesser_block_end("src");
|
let lesser_block_end_specialized = lesser_block_end("src");
|
||||||
let contexts = [
|
let contexts = [
|
||||||
@@ -231,22 +280,44 @@ pub(crate) fn src_block<'b, 'g, 'r, 's>(
|
|||||||
let parser_context = context.with_additional_node(&contexts[0]);
|
let parser_context = context.with_additional_node(&contexts[0]);
|
||||||
let parser_context = parser_context.with_additional_node(&contexts[1]);
|
let parser_context = parser_context.with_additional_node(&contexts[1]);
|
||||||
let parser_context = parser_context.with_additional_node(&contexts[2]);
|
let parser_context = parser_context.with_additional_node(&contexts[2]);
|
||||||
let parameters = match parameters {
|
let (remaining, contents) = content(&parser_context, remaining)?;
|
||||||
Some((_ws, parameters)) => Some(parameters),
|
|
||||||
None => None,
|
|
||||||
};
|
|
||||||
|
|
||||||
let (remaining, contents) = parser_with_context!(text_until_exit)(&parser_context)(remaining)?;
|
|
||||||
let (remaining, _end) = lesser_block_end_specialized(&parser_context, remaining)?;
|
let (remaining, _end) = lesser_block_end_specialized(&parser_context, remaining)?;
|
||||||
|
|
||||||
|
let (remaining, _trailing_ws) =
|
||||||
|
maybe_consume_trailing_whitespace_if_not_exiting(context, remaining)?;
|
||||||
let source = get_consumed(input, remaining);
|
let source = get_consumed(input, remaining);
|
||||||
|
let (switches, number_lines, preserve_indent, retain_labels, use_labels, label_format) = {
|
||||||
|
if let Some(switches) = switches {
|
||||||
|
(
|
||||||
|
if switches.source.len() == 0 {
|
||||||
|
None
|
||||||
|
} else {
|
||||||
|
Some(switches.source)
|
||||||
|
},
|
||||||
|
switches.number_lines,
|
||||||
|
switches.preserve_indent,
|
||||||
|
switches.retain_labels,
|
||||||
|
switches.use_labels,
|
||||||
|
switches.label_format,
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
(None, None, None, RetainLabels::Yes, true, None)
|
||||||
|
}
|
||||||
|
};
|
||||||
Ok((
|
Ok((
|
||||||
remaining,
|
remaining,
|
||||||
SrcBlock {
|
SrcBlock {
|
||||||
source: source.into(),
|
source: source.into(),
|
||||||
name: name.into(),
|
name: get_name(&affiliated_keywords),
|
||||||
data: parameters.map(|parameters| Into::<&str>::into(parameters)),
|
language: language.map(Into::<&str>::into),
|
||||||
contents: contents.into(),
|
switches,
|
||||||
|
parameters: parameters.map(Into::<&str>::into),
|
||||||
|
number_lines,
|
||||||
|
preserve_indent,
|
||||||
|
retain_labels,
|
||||||
|
use_labels,
|
||||||
|
label_format,
|
||||||
|
contents,
|
||||||
},
|
},
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
@@ -309,3 +380,259 @@ fn _lesser_block_begin<'b, 'g, 'r, 's, 'c>(
|
|||||||
))(remaining)?;
|
))(remaining)?;
|
||||||
Ok((remaining, name))
|
Ok((remaining, name))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
struct ExampleSrcSwitches<'s> {
|
||||||
|
source: &'s str,
|
||||||
|
number_lines: Option<SwitchNumberLines>,
|
||||||
|
retain_labels: RetainLabels,
|
||||||
|
preserve_indent: Option<CharOffsetInLine>,
|
||||||
|
use_labels: bool,
|
||||||
|
label_format: Option<&'s str>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
enum SwitchState {
|
||||||
|
Normal,
|
||||||
|
NewLineNumber,
|
||||||
|
ContinuedLineNumber,
|
||||||
|
LabelFormat,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
|
||||||
|
fn src_parameters<'s>(input: OrgSource<'s>) -> Res<OrgSource<'s>, OrgSource<'s>> {
|
||||||
|
verify(
|
||||||
|
recognize(many_till(anychar, peek(tuple((space0, line_ending))))),
|
||||||
|
|parameters: &OrgSource<'_>| parameters.len() > 0,
|
||||||
|
)(input)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
|
||||||
|
fn src_switches<'s>(input: OrgSource<'s>) -> Res<OrgSource<'s>, ExampleSrcSwitches<'s>> {
|
||||||
|
let (remaining, leading_spaces) = space1(input)?;
|
||||||
|
let offset = Into::<&str>::into(leading_spaces).chars().count();
|
||||||
|
let offset = CharOffsetInLine::try_from(offset)
|
||||||
|
.expect("Character offset should fit in CharOffsetInLine");
|
||||||
|
example_src_switches(true, offset)(remaining)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
|
||||||
|
fn example_switches<'s>(input: OrgSource<'s>) -> Res<OrgSource<'s>, ExampleSrcSwitches<'s>> {
|
||||||
|
let (remaining, switches) = example_src_switches(false, 0)(input)?;
|
||||||
|
Ok((remaining, switches))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn example_src_switches(
|
||||||
|
stop_at_parameters: bool,
|
||||||
|
additional_char_offset: CharOffsetInLine,
|
||||||
|
) -> impl for<'s> Fn(OrgSource<'s>) -> Res<OrgSource<'s>, ExampleSrcSwitches<'s>> {
|
||||||
|
move |input| _example_src_switches(input, stop_at_parameters, additional_char_offset)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
|
||||||
|
fn _example_src_switches<'s>(
|
||||||
|
input: OrgSource<'s>,
|
||||||
|
stop_at_parameters: bool,
|
||||||
|
additional_char_offset: CharOffsetInLine,
|
||||||
|
) -> Res<OrgSource<'s>, ExampleSrcSwitches<'s>> {
|
||||||
|
let mut number_lines = None;
|
||||||
|
let mut retain_labels = RetainLabels::Yes;
|
||||||
|
let mut preserve_indent = None;
|
||||||
|
let mut use_labels = true;
|
||||||
|
let mut label_format = None;
|
||||||
|
let mut saw_r = false;
|
||||||
|
let mut matched_a_word = false;
|
||||||
|
let mut remaining = input;
|
||||||
|
let mut last_match_remaining = input;
|
||||||
|
let mut state = SwitchState::Normal;
|
||||||
|
'outer: loop {
|
||||||
|
let (remain, word) = opt(switch_word)(remaining)?;
|
||||||
|
let word = match word {
|
||||||
|
Some(word) => word,
|
||||||
|
None => {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let normalized_word = Into::<&str>::into(word);
|
||||||
|
|
||||||
|
loop {
|
||||||
|
match (&state, normalized_word) {
|
||||||
|
(SwitchState::Normal, "-n") => {
|
||||||
|
state = SwitchState::NewLineNumber;
|
||||||
|
}
|
||||||
|
(SwitchState::Normal, "+n") => {
|
||||||
|
state = SwitchState::ContinuedLineNumber;
|
||||||
|
}
|
||||||
|
(SwitchState::Normal, "-r") => {
|
||||||
|
saw_r = true;
|
||||||
|
use_labels = false;
|
||||||
|
match retain_labels {
|
||||||
|
RetainLabels::Yes => {
|
||||||
|
retain_labels = RetainLabels::No;
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
(SwitchState::Normal, "-l") => {
|
||||||
|
state = SwitchState::LabelFormat;
|
||||||
|
}
|
||||||
|
(SwitchState::Normal, "-k") => {
|
||||||
|
use_labels = false;
|
||||||
|
let text_until_flag = input.get_until(word);
|
||||||
|
let character_offset = Into::<&str>::into(text_until_flag).chars().count();
|
||||||
|
let character_offset = CharOffsetInLine::try_from(character_offset)
|
||||||
|
.expect("Character offset should fit in CharOffsetInLine");
|
||||||
|
retain_labels = RetainLabels::Keep(character_offset + additional_char_offset);
|
||||||
|
}
|
||||||
|
(SwitchState::Normal, "-i") => {
|
||||||
|
let text_until_flag = input.get_until(word);
|
||||||
|
let character_offset = Into::<&str>::into(text_until_flag).chars().count();
|
||||||
|
let character_offset = CharOffsetInLine::try_from(character_offset)
|
||||||
|
.expect("Character offset should fit in CharOffsetInLine");
|
||||||
|
preserve_indent = Some(character_offset + additional_char_offset);
|
||||||
|
}
|
||||||
|
(SwitchState::NewLineNumber, _) => {
|
||||||
|
let val = normalized_word.parse::<LineNumber>();
|
||||||
|
if let Ok(val) = val {
|
||||||
|
if val < 0 {
|
||||||
|
number_lines = Some(SwitchNumberLines::New(0));
|
||||||
|
} else {
|
||||||
|
// Note that this can result in a negative 1 if the val is originally 0.
|
||||||
|
number_lines = Some(SwitchNumberLines::New(val - 1));
|
||||||
|
}
|
||||||
|
state = SwitchState::Normal;
|
||||||
|
} else {
|
||||||
|
number_lines = Some(SwitchNumberLines::New(0));
|
||||||
|
state = SwitchState::Normal;
|
||||||
|
continue; // Re-processes the word
|
||||||
|
}
|
||||||
|
}
|
||||||
|
(SwitchState::ContinuedLineNumber, _) => {
|
||||||
|
let val = normalized_word.parse::<LineNumber>();
|
||||||
|
if let Ok(val) = val {
|
||||||
|
if val < 0 {
|
||||||
|
number_lines = Some(SwitchNumberLines::Continued(0));
|
||||||
|
} else {
|
||||||
|
// Note that this can result in a negative 1 if the val is originally 0.
|
||||||
|
number_lines = Some(SwitchNumberLines::Continued(val - 1));
|
||||||
|
}
|
||||||
|
state = SwitchState::Normal;
|
||||||
|
} else {
|
||||||
|
number_lines = Some(SwitchNumberLines::Continued(0));
|
||||||
|
state = SwitchState::Normal;
|
||||||
|
continue; // Re-processes the word
|
||||||
|
}
|
||||||
|
}
|
||||||
|
(SwitchState::LabelFormat, _) => {
|
||||||
|
label_format = Some(normalized_word);
|
||||||
|
state = SwitchState::Normal;
|
||||||
|
}
|
||||||
|
(SwitchState::Normal, _) if stop_at_parameters => {
|
||||||
|
break 'outer;
|
||||||
|
}
|
||||||
|
(SwitchState::Normal, _) => {}
|
||||||
|
};
|
||||||
|
matched_a_word = true;
|
||||||
|
remaining = remain;
|
||||||
|
last_match_remaining = remain;
|
||||||
|
|
||||||
|
let (remain, divider) = opt(space1)(remaining)?;
|
||||||
|
if divider.is_none() {
|
||||||
|
break 'outer;
|
||||||
|
}
|
||||||
|
remaining = remain;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !matched_a_word {
|
||||||
|
return Err(nom::Err::Error(CustomError::MyError(MyError("No words."))));
|
||||||
|
}
|
||||||
|
let remaining = last_match_remaining;
|
||||||
|
|
||||||
|
let (remaining, _post_spaces) = opt(tuple((space0, peek(line_ending))))(remaining)?;
|
||||||
|
let source = input.get_until(remaining);
|
||||||
|
|
||||||
|
// Handle state that didn't get processed because we ran out of words.
|
||||||
|
match state {
|
||||||
|
SwitchState::Normal => {}
|
||||||
|
SwitchState::NewLineNumber => {
|
||||||
|
number_lines = Some(SwitchNumberLines::New(0));
|
||||||
|
}
|
||||||
|
SwitchState::ContinuedLineNumber => {
|
||||||
|
number_lines = Some(SwitchNumberLines::Continued(0));
|
||||||
|
}
|
||||||
|
SwitchState::LabelFormat => {}
|
||||||
|
}
|
||||||
|
|
||||||
|
let retain_labels = match retain_labels {
|
||||||
|
RetainLabels::Keep(_) if !saw_r => RetainLabels::Yes,
|
||||||
|
_ => retain_labels,
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok((
|
||||||
|
remaining,
|
||||||
|
ExampleSrcSwitches {
|
||||||
|
source: Into::<&str>::into(source),
|
||||||
|
number_lines,
|
||||||
|
retain_labels,
|
||||||
|
preserve_indent,
|
||||||
|
use_labels,
|
||||||
|
label_format,
|
||||||
|
},
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
|
||||||
|
fn switch_word<'s>(input: OrgSource<'s>) -> Res<OrgSource<'s>, OrgSource<'s>> {
|
||||||
|
alt((
|
||||||
|
map(
|
||||||
|
tuple((tag(r#"""#), is_not("\"\r\n"), tag(r#"""#))),
|
||||||
|
|(_, contents, _)| contents,
|
||||||
|
),
|
||||||
|
is_not(" \t\r\n"),
|
||||||
|
))(input)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
|
||||||
|
pub(crate) fn content<'b, 'g, 'r, 's>(
|
||||||
|
context: RefContext<'b, 'g, 'r, 's>,
|
||||||
|
input: OrgSource<'s>,
|
||||||
|
) -> Res<OrgSource<'s>, String> {
|
||||||
|
let mut ret = String::new();
|
||||||
|
let mut remaining = input;
|
||||||
|
let exit_matcher_parser = parser_with_context!(exit_matcher_parser)(context);
|
||||||
|
loop {
|
||||||
|
if exit_matcher_parser(remaining).is_ok() {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
let (remain, (pre_escape_whitespace, line)) = content_line(remaining)?;
|
||||||
|
pre_escape_whitespace.map(|val| ret.push_str(Into::<&str>::into(val)));
|
||||||
|
ret.push_str(line.into());
|
||||||
|
remaining = remain;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok((remaining, ret))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
|
||||||
|
fn content_line<'s>(
|
||||||
|
input: OrgSource<'s>,
|
||||||
|
) -> Res<OrgSource<'s>, (Option<OrgSource<'s>>, OrgSource<'s>)> {
|
||||||
|
let (remaining, pre_escape_whitespace) = opt(map(
|
||||||
|
tuple((
|
||||||
|
recognize(tuple((
|
||||||
|
space0,
|
||||||
|
many_till(
|
||||||
|
tag(","),
|
||||||
|
peek(tuple((tag(","), alt((tag("#+"), tag("*")))))),
|
||||||
|
),
|
||||||
|
))),
|
||||||
|
tag(","),
|
||||||
|
)),
|
||||||
|
|(pre_comma, _)| pre_comma,
|
||||||
|
))(input)?;
|
||||||
|
let (remaining, line_post_escape) = recognize(many_till(anychar, line_ending))(remaining)?;
|
||||||
|
Ok((remaining, (pre_escape_whitespace, line_post_escape)))
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
mod angle_link;
|
mod angle_link;
|
||||||
|
mod babel_call;
|
||||||
mod citation;
|
mod citation;
|
||||||
mod citation_reference;
|
mod citation_reference;
|
||||||
mod clock;
|
mod clock;
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
use std::borrow::Cow;
|
||||||
use std::ops::RangeBounds;
|
use std::ops::RangeBounds;
|
||||||
|
|
||||||
use nom::Compare;
|
use nom::Compare;
|
||||||
@@ -14,7 +15,7 @@ use crate::error::MyError;
|
|||||||
|
|
||||||
pub(crate) type BracketDepth = i16;
|
pub(crate) type BracketDepth = i16;
|
||||||
|
|
||||||
#[derive(Copy, Clone)]
|
#[derive(Copy, Clone, PartialEq)]
|
||||||
pub(crate) struct OrgSource<'s> {
|
pub(crate) struct OrgSource<'s> {
|
||||||
full_source: &'s str,
|
full_source: &'s str,
|
||||||
start: usize,
|
start: usize,
|
||||||
@@ -184,6 +185,18 @@ impl<'s> From<OrgSource<'s>> for &'s str {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<'s> From<&OrgSource<'s>> for Cow<'s, str> {
|
||||||
|
fn from(value: &OrgSource<'s>) -> Self {
|
||||||
|
(&value.full_source[value.start..value.end]).into()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'s> From<OrgSource<'s>> for Cow<'s, str> {
|
||||||
|
fn from(value: OrgSource<'s>) -> Self {
|
||||||
|
(&value.full_source[value.start..value.end]).into()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl<'s, R> Slice<R> for OrgSource<'s>
|
impl<'s, R> Slice<R> for OrgSource<'s>
|
||||||
where
|
where
|
||||||
R: RangeBounds<usize>,
|
R: RangeBounds<usize>,
|
||||||
|
|||||||
@@ -2,19 +2,27 @@ use nom::branch::alt;
|
|||||||
use nom::combinator::eof;
|
use nom::combinator::eof;
|
||||||
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::element_parser::detect_element;
|
use super::element_parser::detect_element;
|
||||||
|
use super::keyword::affiliated_keyword;
|
||||||
use super::org_source::OrgSource;
|
use super::org_source::OrgSource;
|
||||||
use super::util::blank_line;
|
use super::util::blank_line;
|
||||||
use super::util::get_consumed;
|
use super::util::get_consumed;
|
||||||
|
use super::util::get_has_affiliated_keyword;
|
||||||
|
use super::util::get_name;
|
||||||
|
use super::util::maybe_consume_trailing_whitespace_if_not_exiting;
|
||||||
use crate::context::parser_with_context;
|
use crate::context::parser_with_context;
|
||||||
use crate::context::ContextElement;
|
use crate::context::ContextElement;
|
||||||
use crate::context::ExitClass;
|
use crate::context::ExitClass;
|
||||||
use crate::context::ExitMatcherNode;
|
use crate::context::ExitMatcherNode;
|
||||||
|
use crate::context::HasAffiliatedKeywordInner;
|
||||||
use crate::context::RefContext;
|
use crate::context::RefContext;
|
||||||
|
use crate::error::CustomError;
|
||||||
|
use crate::error::MyError;
|
||||||
use crate::error::Res;
|
use crate::error::Res;
|
||||||
use crate::parser::object_parser::standard_set_object;
|
use crate::parser::object_parser::standard_set_object;
|
||||||
use crate::parser::util::exit_matcher_parser;
|
use crate::parser::util::exit_matcher_parser;
|
||||||
@@ -26,27 +34,38 @@ pub(crate) fn paragraph<'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>, Paragraph<'s>> {
|
) -> Res<OrgSource<'s>, Paragraph<'s>> {
|
||||||
let parser_context = ContextElement::ExitMatcherNode(ExitMatcherNode {
|
let (remaining, affiliated_keywords) = many0(affiliated_keyword)(input)?;
|
||||||
class: ExitClass::Gamma,
|
let contexts = [
|
||||||
exit_matcher: ¶graph_end,
|
ContextElement::HasAffiliatedKeyword(HasAffiliatedKeywordInner {
|
||||||
});
|
start_after_affiliated_keywords: remaining,
|
||||||
let parser_context = context.with_additional_node(&parser_context);
|
keywords: &affiliated_keywords,
|
||||||
|
}),
|
||||||
|
ContextElement::ExitMatcherNode(ExitMatcherNode {
|
||||||
|
class: ExitClass::Gamma,
|
||||||
|
exit_matcher: ¶graph_end,
|
||||||
|
}),
|
||||||
|
];
|
||||||
|
let parser_context = context.with_additional_node(&contexts[0]);
|
||||||
|
let parser_context = parser_context.with_additional_node(&contexts[1]);
|
||||||
let standard_set_object_matcher = parser_with_context!(standard_set_object)(&parser_context);
|
let standard_set_object_matcher = parser_with_context!(standard_set_object)(&parser_context);
|
||||||
let exit_matcher = parser_with_context!(exit_matcher_parser)(&parser_context);
|
let exit_matcher = parser_with_context!(exit_matcher_parser)(&parser_context);
|
||||||
|
|
||||||
let (remaining, (children, _exit_contents)) = verify(
|
let (remaining, (children, _exit_contents)) = verify(
|
||||||
many_till(standard_set_object_matcher, exit_matcher),
|
many_till(standard_set_object_matcher, exit_matcher),
|
||||||
|(children, _exit_contents)| !children.is_empty(),
|
|(children, _exit_contents)| !children.is_empty(),
|
||||||
)(input)?;
|
)(remaining)?;
|
||||||
|
|
||||||
// Not checking parent exit matcher because if there are any children matched then we have a valid paragraph.
|
// Not checking parent exit matcher because if there are any children matched then we have a valid paragraph.
|
||||||
|
|
||||||
|
let (remaining, _trailing_ws) =
|
||||||
|
maybe_consume_trailing_whitespace_if_not_exiting(context, remaining)?;
|
||||||
let source = get_consumed(input, remaining);
|
let source = get_consumed(input, remaining);
|
||||||
|
|
||||||
Ok((
|
Ok((
|
||||||
remaining,
|
remaining,
|
||||||
Paragraph {
|
Paragraph {
|
||||||
source: source.into(),
|
source: source.into(),
|
||||||
|
name: get_name(&affiliated_keywords),
|
||||||
children,
|
children,
|
||||||
},
|
},
|
||||||
))
|
))
|
||||||
@@ -57,10 +76,21 @@ fn paragraph_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>> {
|
||||||
let non_paragraph_element_matcher = parser_with_context!(detect_element(false))(context);
|
let regular_end = recognize(tuple((start_of_line, many1(blank_line))))(input);
|
||||||
|
if regular_end.is_ok() {
|
||||||
|
return regular_end;
|
||||||
|
}
|
||||||
|
match get_has_affiliated_keyword(context) {
|
||||||
|
Some(start_post_affiliated_keywords) if input == start_post_affiliated_keywords => {
|
||||||
|
return Err(nom::Err::Error(CustomError::MyError(MyError(
|
||||||
|
"No exit due to affiliated keywords.",
|
||||||
|
))));
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
// Check to see if input is the start of a HasAffiliatedKeyword
|
||||||
alt((
|
alt((
|
||||||
recognize(tuple((start_of_line, many1(blank_line)))),
|
recognize(parser_with_context!(detect_element(false))(context)),
|
||||||
recognize(non_paragraph_element_matcher),
|
|
||||||
eof,
|
eof,
|
||||||
))(input)
|
))(input)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -32,33 +32,6 @@ use crate::parser::util::get_consumed;
|
|||||||
use crate::parser::util::WORD_CONSTITUENT_CHARACTERS;
|
use crate::parser::util::WORD_CONSTITUENT_CHARACTERS;
|
||||||
use crate::types::PlainLink;
|
use crate::types::PlainLink;
|
||||||
|
|
||||||
// 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(crate) fn plain_link<'b, 'g, 'r, 's>(
|
pub(crate) fn plain_link<'b, 'g, 'r, 's>(
|
||||||
context: RefContext<'b, 'g, 'r, 's>,
|
context: RefContext<'b, 'g, 'r, 's>,
|
||||||
@@ -113,12 +86,11 @@ fn post<'b, 'g, 'r, 's>(
|
|||||||
|
|
||||||
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
|
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
|
||||||
pub(crate) fn protocol<'b, 'g, 'r, 's>(
|
pub(crate) fn protocol<'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: This should be defined by org-link-parameters
|
for link_parameter in context.get_global_settings().link_parameters {
|
||||||
for link_parameter in ORG_LINK_PARAMETERS {
|
let result = tag_no_case::<_, _, CustomError<_>>(*link_parameter)(input);
|
||||||
let result = tag_no_case::<_, _, CustomError<_>>(link_parameter)(input);
|
|
||||||
match result {
|
match result {
|
||||||
Ok((remaining, ent)) => {
|
Ok((remaining, ent)) => {
|
||||||
return Ok((remaining, ent));
|
return Ok((remaining, ent));
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user