Compare commits
1 Commits
cc86591a6c
...
feature_ma
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
678106bb65 |
@@ -14,10 +14,6 @@ spec:
|
|||||||
- name: path-to-dockerfile
|
- name: path-to-dockerfile
|
||||||
description: The path to the Dockerfile
|
description: The path to the Dockerfile
|
||||||
type: string
|
type: string
|
||||||
- name: command
|
|
||||||
type: array
|
|
||||||
description: Command to run.
|
|
||||||
default: []
|
|
||||||
tasks:
|
tasks:
|
||||||
- name: report-pending
|
- name: report-pending
|
||||||
taskRef:
|
taskRef:
|
||||||
@@ -81,9 +77,19 @@ spec:
|
|||||||
workspace: docker-credentials
|
workspace: docker-credentials
|
||||||
runAfter:
|
runAfter:
|
||||||
- fetch-repository
|
- fetch-repository
|
||||||
- name: run-image-none
|
- name: build-organic
|
||||||
taskRef:
|
taskRef:
|
||||||
name: run-docker-image
|
name: run-docker-image
|
||||||
|
matrix:
|
||||||
|
params:
|
||||||
|
- name: feature-compare
|
||||||
|
value:
|
||||||
|
- "true"
|
||||||
|
- "false"
|
||||||
|
- name: feature-tracing
|
||||||
|
value:
|
||||||
|
- "true"
|
||||||
|
- "false"
|
||||||
workspaces:
|
workspaces:
|
||||||
- name: source
|
- name: source
|
||||||
workspace: git-source
|
workspace: git-source
|
||||||
@@ -93,77 +99,21 @@ spec:
|
|||||||
- build-image
|
- build-image
|
||||||
params:
|
params:
|
||||||
- name: command
|
- name: command
|
||||||
value: ["$(params.command[*])"]
|
value: ["/bin/sh", "-c"]
|
||||||
- name: args
|
- name: args
|
||||||
value: ["--no-default-features"]
|
value:
|
||||||
- name: docker-image
|
- |
|
||||||
value: "$(params.image-name):$(tasks.fetch-repository.results.commit)"
|
set -euo pipefail
|
||||||
- name: run-image-tracing
|
IFS=$$'\n\t'
|
||||||
taskRef:
|
features=()
|
||||||
name: run-docker-image
|
if [ $(params.feature-compare) = "true" ]; then features+=(compare); fi
|
||||||
workspaces:
|
if [ $(params.feature-tracing) = "true" ]; then features+=(tracing); fi
|
||||||
- name: source
|
if [ $${#features[@]} -eq 0 ]; then
|
||||||
workspace: git-source
|
exec cargo build --no-default-features
|
||||||
- name: cargo-cache
|
else
|
||||||
workspace: cargo-cache
|
featurelist=$$(IFS="," ; echo "$${features[*]}")
|
||||||
runAfter:
|
exec cargo build --no-default-features --features "$$featurelist"
|
||||||
- run-image-none
|
fi
|
||||||
params:
|
|
||||||
- name: command
|
|
||||||
value: ["$(params.command[*])"]
|
|
||||||
- name: args
|
|
||||||
value: ["--no-default-features", "--features", "tracing"]
|
|
||||||
- name: docker-image
|
|
||||||
value: "$(params.image-name):$(tasks.fetch-repository.results.commit)"
|
|
||||||
- name: run-image-compare
|
|
||||||
taskRef:
|
|
||||||
name: run-docker-image
|
|
||||||
workspaces:
|
|
||||||
- name: source
|
|
||||||
workspace: git-source
|
|
||||||
- name: cargo-cache
|
|
||||||
workspace: cargo-cache
|
|
||||||
runAfter:
|
|
||||||
- run-image-tracing
|
|
||||||
params:
|
|
||||||
- name: command
|
|
||||||
value: ["$(params.command[*])"]
|
|
||||||
- name: args
|
|
||||||
value: ["--no-default-features", "--features", "compare"]
|
|
||||||
- name: docker-image
|
|
||||||
value: "$(params.image-name):$(tasks.fetch-repository.results.commit)"
|
|
||||||
- name: run-image-default
|
|
||||||
taskRef:
|
|
||||||
name: run-docker-image
|
|
||||||
workspaces:
|
|
||||||
- name: source
|
|
||||||
workspace: git-source
|
|
||||||
- name: cargo-cache
|
|
||||||
workspace: cargo-cache
|
|
||||||
runAfter:
|
|
||||||
- run-image-compare
|
|
||||||
params:
|
|
||||||
- name: command
|
|
||||||
value: ["$(params.command[*])"]
|
|
||||||
- name: args
|
|
||||||
value: []
|
|
||||||
- name: docker-image
|
|
||||||
value: "$(params.image-name):$(tasks.fetch-repository.results.commit)"
|
|
||||||
- name: run-image-all
|
|
||||||
taskRef:
|
|
||||||
name: run-docker-image
|
|
||||||
workspaces:
|
|
||||||
- name: source
|
|
||||||
workspace: git-source
|
|
||||||
- name: cargo-cache
|
|
||||||
workspace: cargo-cache
|
|
||||||
runAfter:
|
|
||||||
- run-image-default
|
|
||||||
params:
|
|
||||||
- name: command
|
|
||||||
value: ["$(params.command[*])"]
|
|
||||||
- name: args
|
|
||||||
value: ["--no-default-features", "--features", "tracing,compare"]
|
|
||||||
- name: docker-image
|
- name: docker-image
|
||||||
value: "$(params.image-name):$(tasks.fetch-repository.results.commit)"
|
value: "$(params.image-name):$(tasks.fetch-repository.results.commit)"
|
||||||
finally:
|
finally:
|
||||||
@@ -256,5 +206,3 @@ spec:
|
|||||||
value: docker/organic_build/
|
value: docker/organic_build/
|
||||||
- name: path-to-dockerfile
|
- name: path-to-dockerfile
|
||||||
value: docker/organic_build/Dockerfile
|
value: docker/organic_build/Dockerfile
|
||||||
- name: command
|
|
||||||
value: [cargo, build]
|
|
||||||
|
|||||||
@@ -44,13 +44,11 @@ default = ["compare"]
|
|||||||
compare = []
|
compare = []
|
||||||
tracing = ["dep:opentelemetry", "dep:opentelemetry-otlp", "dep:opentelemetry-semantic-conventions", "dep:tokio", "dep:tracing", "dep:tracing-opentelemetry", "dep:tracing-subscriber"]
|
tracing = ["dep:opentelemetry", "dep:opentelemetry-otlp", "dep:opentelemetry-semantic-conventions", "dep:tokio", "dep:tracing", "dep:tracing-opentelemetry", "dep:tracing-subscriber"]
|
||||||
|
|
||||||
# Optimized build for any sort of release.
|
|
||||||
[profile.release-lto]
|
[profile.release-lto]
|
||||||
inherits = "release"
|
inherits = "release"
|
||||||
lto = true
|
lto = true
|
||||||
strip = "symbols"
|
strip = "symbols"
|
||||||
|
|
||||||
# Profile for performance testing with the "perf" tool. Notably keeps debug enabled and does not strip symbols to make reading the perf output easier.
|
|
||||||
[profile.perf]
|
[profile.perf]
|
||||||
inherits = "release"
|
inherits = "release"
|
||||||
lto = true
|
lto = true
|
||||||
|
|||||||
@@ -1,25 +0,0 @@
|
|||||||
#+BEGIN: clocktable :scope file :maxlevel 2
|
|
||||||
#+CAPTION: Clock summary at [2023-08-25 Fri 05:34]
|
|
||||||
| Headline | Time |
|
|
||||||
|--------------+--------|
|
|
||||||
| *Total time* | *0:00* |
|
|
||||||
#+END:
|
|
||||||
|
|
||||||
#+BEGIN: columnview :hlines 1 :id global
|
|
||||||
| ITEM | TODO | PRIORITY | TAGS |
|
|
||||||
|-------+------+----------+------------------------------|
|
|
||||||
| Foo | | B | |
|
|
||||||
|-------+------+----------+------------------------------|
|
|
||||||
| Bar | TODO | B | |
|
|
||||||
|-------+------+----------+------------------------------|
|
|
||||||
| Baz | | B | :thisisatag: |
|
|
||||||
| Lorem | | B | :thisshouldinheritfromabove: |
|
|
||||||
| Ipsum | | B | :multiple:tags: |
|
|
||||||
#+END:
|
|
||||||
* Foo
|
|
||||||
* TODO Bar
|
|
||||||
* Baz :thisisatag:
|
|
||||||
** Lorem :thisshouldinheritfromabove:
|
|
||||||
*** Ipsum :multiple:tags:
|
|
||||||
* Dolar ::
|
|
||||||
* cat :dog: bat
|
|
||||||
@@ -1,7 +0,0 @@
|
|||||||
1. foo
|
|
||||||
2.
|
|
||||||
bar
|
|
||||||
1.
|
|
||||||
#+begin_center
|
|
||||||
Still in the list
|
|
||||||
#+end_center
|
|
||||||
@@ -8,22 +8,11 @@ DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
|
|||||||
|
|
||||||
cd "$DIR/../"
|
cd "$DIR/../"
|
||||||
|
|
||||||
function main {
|
cargo build --profile "$PROFILE" --no-default-features
|
||||||
local additional_flags=()
|
perf record --freq=2000 --call-graph dwarf --output=perf.data target/${PROFILE}/compare
|
||||||
if [ "$PROFILE" = "dev" ] || [ "$PROFILE" = "debug" ]; then
|
# Convert to a format firefox will read
|
||||||
PROFILE="debug"
|
# flags to consider --show-info
|
||||||
else
|
perf script -F +pid --input perf.data > perf.firefox
|
||||||
additional_flags+=(--profile "$PROFILE")
|
|
||||||
fi
|
|
||||||
cargo build --no-default-features "${additional_flags[@]}"
|
|
||||||
perf record --freq=2000 --call-graph dwarf --output=perf.data target/${PROFILE}/compare
|
|
||||||
|
|
||||||
# Convert to a format firefox will read
|
echo "You probably want to go to https://profiler.firefox.com/"
|
||||||
# flags to consider --show-info
|
echo "Either that or run hotspot"
|
||||||
perf script -F +pid --input perf.data > perf.firefox
|
|
||||||
|
|
||||||
echo "You probably want to go to https://profiler.firefox.com/"
|
|
||||||
echo "Either that or run hotspot"
|
|
||||||
}
|
|
||||||
|
|
||||||
main "${@}"
|
|
||||||
|
|||||||
@@ -7,8 +7,6 @@ DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
|
|||||||
: ${SHELL:="NO"} # or YES to launch a shell instead of running the test
|
: ${SHELL:="NO"} # or YES to launch a shell instead of running the test
|
||||||
: ${TRACE:="NO"} # or YES to send traces to jaeger
|
: ${TRACE:="NO"} # or YES to send traces to jaeger
|
||||||
: ${BACKTRACE:="NO"} # or YES to print a rust backtrace when panicking
|
: ${BACKTRACE:="NO"} # or YES to print a rust backtrace when panicking
|
||||||
: ${NO_COLOR:=""} # Set to anything to disable color output
|
|
||||||
|
|
||||||
|
|
||||||
cd "$DIR/../"
|
cd "$DIR/../"
|
||||||
REALPATH=$(command -v uu-realpath || command -v realpath)
|
REALPATH=$(command -v uu-realpath || command -v realpath)
|
||||||
@@ -27,10 +25,6 @@ function launch_container {
|
|||||||
local additional_flags=()
|
local additional_flags=()
|
||||||
local additional_args=()
|
local additional_args=()
|
||||||
|
|
||||||
if [ "$NO_COLOR" != "" ]; then
|
|
||||||
additional_flags+=(--env "NO_COLOR=$NO_COLOR")
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [ "$SHELL" != "YES" ]; then
|
if [ "$SHELL" != "YES" ]; then
|
||||||
additional_args+=(cargo run)
|
additional_args+=(cargo run)
|
||||||
else
|
else
|
||||||
|
|||||||
@@ -4,8 +4,6 @@ set -euo pipefail
|
|||||||
IFS=$'\n\t'
|
IFS=$'\n\t'
|
||||||
DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
|
DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
|
||||||
|
|
||||||
: ${NO_COLOR:=""} # Set to anything to disable color output
|
|
||||||
|
|
||||||
cd "$DIR/../"
|
cd "$DIR/../"
|
||||||
REALPATH=$(command -v uu-realpath || command -v realpath)
|
REALPATH=$(command -v uu-realpath || command -v realpath)
|
||||||
MAKE=$(command -v gmake || command -v make)
|
MAKE=$(command -v gmake || command -v make)
|
||||||
@@ -42,11 +40,7 @@ function get_test_names {
|
|||||||
|
|
||||||
function launch_container {
|
function launch_container {
|
||||||
local test="$1"
|
local test="$1"
|
||||||
local additional_flags=()
|
local additional_args=()
|
||||||
|
|
||||||
if [ "$NO_COLOR" != "" ]; then
|
|
||||||
additional_flags+=(--env "NO_COLOR=$NO_COLOR")
|
|
||||||
fi
|
|
||||||
|
|
||||||
local init_script=$(cat <<EOF
|
local init_script=$(cat <<EOF
|
||||||
set -euo pipefail
|
set -euo pipefail
|
||||||
@@ -56,7 +50,7 @@ cargo test --no-fail-fast --lib --test test_loader "$test" -- --show-output
|
|||||||
EOF
|
EOF
|
||||||
)
|
)
|
||||||
|
|
||||||
docker run "${additional_flags[@]}" --init --rm -v "$($REALPATH ./):/source:ro" --mount source=cargo-cache,target=/usr/local/cargo/registry --mount source=rust-cache,target=/target --env CARGO_TARGET_DIR=/target -w /source organic-test sh -c "$init_script"
|
docker run --init --rm -v "$($REALPATH ./):/source:ro" --mount source=cargo-cache,target=/usr/local/cargo/registry --mount source=rust-cache,target=/target --env CARGO_TARGET_DIR=/target -w /source organic-test sh -c "$init_script"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,23 +0,0 @@
|
|||||||
#!/usr/bin/env bash
|
|
||||||
#
|
|
||||||
# Time running a single parse without invoking a compare with emacs.
|
|
||||||
set -euo pipefail
|
|
||||||
IFS=$'\n\t'
|
|
||||||
DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
|
|
||||||
|
|
||||||
: ${PROFILE:="release-lto"}
|
|
||||||
|
|
||||||
cd "$DIR/../"
|
|
||||||
|
|
||||||
function main {
|
|
||||||
local additional_flags=()
|
|
||||||
if [ "$PROFILE" = "dev" ] || [ "$PROFILE" = "debug" ]; then
|
|
||||||
PROFILE="debug"
|
|
||||||
else
|
|
||||||
additional_flags+=(--profile "$PROFILE")
|
|
||||||
fi
|
|
||||||
cargo build --no-default-features "${additional_flags[@]}"
|
|
||||||
time ./target/${PROFILE}/compare
|
|
||||||
}
|
|
||||||
|
|
||||||
main "${@}"
|
|
||||||
@@ -1,8 +1,5 @@
|
|||||||
use std::collections::HashSet;
|
|
||||||
|
|
||||||
use super::util::assert_bounds;
|
use super::util::assert_bounds;
|
||||||
use super::util::assert_name;
|
use super::util::assert_name;
|
||||||
use crate::parser::sexp::unquote;
|
|
||||||
use crate::parser::sexp::Token;
|
use crate::parser::sexp::Token;
|
||||||
use crate::parser::AngleLink;
|
use crate::parser::AngleLink;
|
||||||
use crate::parser::Bold;
|
use crate::parser::Bold;
|
||||||
@@ -87,24 +84,12 @@ impl DiffResult {
|
|||||||
match self.status {
|
match self.status {
|
||||||
DiffStatus::Good => {
|
DiffStatus::Good => {
|
||||||
if self.has_bad_children() {
|
if self.has_bad_children() {
|
||||||
format!(
|
"BADCHILD"
|
||||||
"{color}BADCHILD{reset}",
|
|
||||||
color = DiffResult::foreground_color(255, 255, 0),
|
|
||||||
reset = DiffResult::reset_color(),
|
|
||||||
)
|
|
||||||
} else {
|
} else {
|
||||||
format!(
|
"GOOD"
|
||||||
"{color}GOOD{reset}",
|
|
||||||
color = DiffResult::foreground_color(0, 255, 0),
|
|
||||||
reset = DiffResult::reset_color(),
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
DiffStatus::Bad => format!(
|
DiffStatus::Bad => "BAD",
|
||||||
"{color}BAD{reset}",
|
|
||||||
color = DiffResult::foreground_color(255, 0, 0),
|
|
||||||
reset = DiffResult::reset_color(),
|
|
||||||
),
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
println!(
|
println!(
|
||||||
@@ -132,45 +117,6 @@ impl DiffResult {
|
|||||||
DiffStatus::Bad => true,
|
DiffStatus::Bad => true,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn foreground_color(red: u8, green: u8, blue: u8) -> String {
|
|
||||||
if DiffResult::should_use_color() {
|
|
||||||
format!(
|
|
||||||
"\x1b[38;2;{red};{green};{blue}m",
|
|
||||||
red = red,
|
|
||||||
green = green,
|
|
||||||
blue = blue
|
|
||||||
)
|
|
||||||
} else {
|
|
||||||
String::new()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[allow(dead_code)]
|
|
||||||
fn background_color(red: u8, green: u8, blue: u8) -> String {
|
|
||||||
if DiffResult::should_use_color() {
|
|
||||||
format!(
|
|
||||||
"\x1b[48;2;{red};{green};{blue}m",
|
|
||||||
red = red,
|
|
||||||
green = green,
|
|
||||||
blue = blue
|
|
||||||
)
|
|
||||||
} else {
|
|
||||||
String::new()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn reset_color() -> &'static str {
|
|
||||||
if DiffResult::should_use_color() {
|
|
||||||
"\x1b[0m"
|
|
||||||
} else {
|
|
||||||
""
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn should_use_color() -> bool {
|
|
||||||
!std::env::var("NO_COLOR").is_ok_and(|val| !val.is_empty())
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn compare_element<'s>(
|
fn compare_element<'s>(
|
||||||
@@ -326,7 +272,6 @@ fn compare_heading<'s>(
|
|||||||
let children = emacs.as_list()?;
|
let children = emacs.as_list()?;
|
||||||
let mut child_status = Vec::new();
|
let mut child_status = Vec::new();
|
||||||
let mut this_status = DiffStatus::Good;
|
let mut this_status = DiffStatus::Good;
|
||||||
let mut message = None;
|
|
||||||
let emacs_name = "headline";
|
let emacs_name = "headline";
|
||||||
if assert_name(emacs, emacs_name).is_err() {
|
if assert_name(emacs, emacs_name).is_err() {
|
||||||
this_status = DiffStatus::Bad;
|
this_status = DiffStatus::Bad;
|
||||||
@@ -336,45 +281,6 @@ fn compare_heading<'s>(
|
|||||||
this_status = DiffStatus::Bad;
|
this_status = DiffStatus::Bad;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Compare tags
|
|
||||||
let emacs_tags = get_tags_from_heading(emacs)?;
|
|
||||||
let emacs_tags: HashSet<_> = emacs_tags.iter().map(|val| val.as_str()).collect();
|
|
||||||
let rust_tags: HashSet<&str> = rust.tags.iter().map(|val| *val).collect();
|
|
||||||
let difference: Vec<&str> = emacs_tags
|
|
||||||
.symmetric_difference(&rust_tags)
|
|
||||||
.map(|val| *val)
|
|
||||||
.collect();
|
|
||||||
if !difference.is_empty() {
|
|
||||||
this_status = DiffStatus::Bad;
|
|
||||||
message = Some(format!("Mismatched tags: {}", difference.join(", ")));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Compare todo-keyword
|
|
||||||
let todo_keyword = {
|
|
||||||
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 todo_keyword = attributes_map
|
|
||||||
.get(":todo-keyword")
|
|
||||||
.ok_or("Missing :todo-keyword attribute.");
|
|
||||||
todo_keyword?.as_atom()?
|
|
||||||
};
|
|
||||||
match (todo_keyword, rust.todo_keyword, unquote(todo_keyword)) {
|
|
||||||
("nil", None, _) => {}
|
|
||||||
(_, Some(rust_todo), Ok(emacs_todo)) if emacs_todo == rust_todo => {}
|
|
||||||
(emacs_todo, rust_todo, _) => {
|
|
||||||
this_status = DiffStatus::Bad;
|
|
||||||
message = Some(format!(
|
|
||||||
"(emacs != rust) {:?} != {:?}",
|
|
||||||
emacs_todo, rust_todo
|
|
||||||
));
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// Compare title
|
|
||||||
let title = {
|
let title = {
|
||||||
let children = emacs.as_list()?;
|
let children = emacs.as_list()?;
|
||||||
let attributes_child = children
|
let attributes_child = children
|
||||||
@@ -391,9 +297,6 @@ fn compare_heading<'s>(
|
|||||||
child_status.push(compare_object(source, emacs_child, rust_child)?);
|
child_status.push(compare_object(source, emacs_child, rust_child)?);
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Compare todo-type, level, priority
|
|
||||||
|
|
||||||
// Compare section
|
|
||||||
for (emacs_child, rust_child) in children.iter().skip(2).zip(rust.children.iter()) {
|
for (emacs_child, rust_child) in children.iter().skip(2).zip(rust.children.iter()) {
|
||||||
match rust_child {
|
match rust_child {
|
||||||
DocumentElement::Heading(rust_heading) => {
|
DocumentElement::Heading(rust_heading) => {
|
||||||
@@ -408,44 +311,11 @@ fn compare_heading<'s>(
|
|||||||
Ok(DiffResult {
|
Ok(DiffResult {
|
||||||
status: this_status,
|
status: this_status,
|
||||||
name: emacs_name.to_owned(),
|
name: emacs_name.to_owned(),
|
||||||
message,
|
message: None,
|
||||||
children: child_status,
|
children: child_status,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_tags_from_heading<'s>(
|
|
||||||
emacs: &'s Token<'s>,
|
|
||||||
) -> Result<HashSet<String>, Box<dyn std::error::Error>> {
|
|
||||||
let children = emacs.as_list()?;
|
|
||||||
let attributes_child = children
|
|
||||||
.iter()
|
|
||||||
.nth(1)
|
|
||||||
.ok_or("Should have an attributes child.")?;
|
|
||||||
let attributes_map = attributes_child.as_map()?;
|
|
||||||
let tags = attributes_map
|
|
||||||
.get(":tags")
|
|
||||||
.ok_or("Missing :tags attribute.")?;
|
|
||||||
match tags.as_atom() {
|
|
||||||
Ok("nil") => {
|
|
||||||
return Ok(HashSet::new());
|
|
||||||
}
|
|
||||||
Ok(val) => panic!("Unexpected value for tags: {:?}", val),
|
|
||||||
Err(_) => {}
|
|
||||||
};
|
|
||||||
let tags = {
|
|
||||||
let tags = tags.as_list()?;
|
|
||||||
let strings = tags
|
|
||||||
.iter()
|
|
||||||
.map(Token::as_atom)
|
|
||||||
.collect::<Result<Vec<&str>, _>>()?;
|
|
||||||
strings
|
|
||||||
.into_iter()
|
|
||||||
.map(unquote)
|
|
||||||
.collect::<Result<HashSet<String>, _>>()?
|
|
||||||
};
|
|
||||||
Ok(tags)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn compare_paragraph<'s>(
|
fn compare_paragraph<'s>(
|
||||||
source: &'s str,
|
source: &'s str,
|
||||||
emacs: &'s Token<'s>,
|
emacs: &'s Token<'s>,
|
||||||
@@ -1104,7 +974,7 @@ fn compare_plain_text<'s>(
|
|||||||
rust.source.len()
|
rust.source.len()
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
let unquoted_text = unquote(text.text)?;
|
let unquoted_text = text.unquote()?;
|
||||||
if unquoted_text != rust.source {
|
if unquoted_text != rust.source {
|
||||||
this_status = DiffStatus::Bad;
|
this_status = DiffStatus::Bad;
|
||||||
message = Some(format!(
|
message = Some(format!(
|
||||||
|
|||||||
@@ -47,7 +47,9 @@ pub fn assert_bounds<'s, S: Source<'s>>(
|
|||||||
standard_properties
|
standard_properties
|
||||||
.begin
|
.begin
|
||||||
.ok_or("Token should have a begin.")?,
|
.ok_or("Token should have a begin.")?,
|
||||||
standard_properties.end.ok_or("Token should have an end.")?,
|
standard_properties
|
||||||
|
.end
|
||||||
|
.ok_or("Token should have a begin.")?,
|
||||||
);
|
);
|
||||||
let (rust_begin, rust_end) = get_offsets(source, rust);
|
let (rust_begin, rust_end) = get_offsets(source, rust);
|
||||||
if (rust_begin + 1) != begin || (rust_end + 1) != end {
|
if (rust_begin + 1) != begin || (rust_end + 1) != end {
|
||||||
|
|||||||
@@ -1,8 +1,6 @@
|
|||||||
use nom::branch::alt;
|
use nom::branch::alt;
|
||||||
use nom::bytes::complete::tag;
|
use nom::bytes::complete::tag;
|
||||||
use nom::character::complete::anychar;
|
|
||||||
use nom::character::complete::line_ending;
|
use nom::character::complete::line_ending;
|
||||||
use nom::character::complete::space0;
|
|
||||||
use nom::character::complete::space1;
|
use nom::character::complete::space1;
|
||||||
use nom::combinator::eof;
|
use nom::combinator::eof;
|
||||||
use nom::combinator::map;
|
use nom::combinator::map;
|
||||||
@@ -14,7 +12,6 @@ use nom::multi::many0;
|
|||||||
use nom::multi::many1;
|
use nom::multi::many1;
|
||||||
use nom::multi::many1_count;
|
use nom::multi::many1_count;
|
||||||
use nom::multi::many_till;
|
use nom::multi::many_till;
|
||||||
use nom::multi::separated_list1;
|
|
||||||
use nom::sequence::tuple;
|
use nom::sequence::tuple;
|
||||||
|
|
||||||
use super::element::Element;
|
use super::element::Element;
|
||||||
@@ -53,10 +50,7 @@ pub struct Document<'s> {
|
|||||||
pub struct Heading<'s> {
|
pub struct Heading<'s> {
|
||||||
pub source: &'s str,
|
pub source: &'s str,
|
||||||
pub stars: usize,
|
pub stars: usize,
|
||||||
pub todo_keyword: Option<&'s str>,
|
|
||||||
// TODO: add todo-type enum
|
|
||||||
pub title: Vec<Object<'s>>,
|
pub title: Vec<Object<'s>>,
|
||||||
pub tags: Vec<&'s str>,
|
|
||||||
pub children: Vec<DocumentElement<'s>>,
|
pub children: Vec<DocumentElement<'s>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -273,8 +267,7 @@ fn heading<'r, 's>(
|
|||||||
input: OrgSource<'s>,
|
input: OrgSource<'s>,
|
||||||
) -> Res<OrgSource<'s>, Heading<'s>> {
|
) -> Res<OrgSource<'s>, Heading<'s>> {
|
||||||
not(|i| context.check_exit_matcher(i))(input)?;
|
not(|i| context.check_exit_matcher(i))(input)?;
|
||||||
let (remaining, (star_count, _ws, maybe_todo_keyword, title, heading_tags)) =
|
let (remaining, (star_count, _ws, title)) = headline(context, input)?;
|
||||||
headline(context, input)?;
|
|
||||||
let section_matcher = parser_with_context!(section)(context);
|
let section_matcher = parser_with_context!(section)(context);
|
||||||
let heading_matcher = parser_with_context!(heading)(context);
|
let heading_matcher = parser_with_context!(heading)(context);
|
||||||
let (remaining, children) = many0(alt((
|
let (remaining, children) = many0(alt((
|
||||||
@@ -290,10 +283,7 @@ fn heading<'r, 's>(
|
|||||||
Heading {
|
Heading {
|
||||||
source: source.into(),
|
source: source.into(),
|
||||||
stars: star_count,
|
stars: star_count,
|
||||||
todo_keyword: maybe_todo_keyword
|
|
||||||
.map(|(todo_keyword, _ws)| Into::<&str>::into(todo_keyword)),
|
|
||||||
title,
|
title,
|
||||||
tags: heading_tags,
|
|
||||||
children,
|
children,
|
||||||
},
|
},
|
||||||
))
|
))
|
||||||
@@ -303,83 +293,30 @@ fn heading<'r, 's>(
|
|||||||
fn headline<'r, 's>(
|
fn headline<'r, 's>(
|
||||||
context: Context<'r, 's>,
|
context: Context<'r, 's>,
|
||||||
input: OrgSource<'s>,
|
input: OrgSource<'s>,
|
||||||
) -> Res<
|
) -> Res<OrgSource<'s>, (usize, OrgSource<'s>, Vec<Object<'s>>)> {
|
||||||
OrgSource<'s>,
|
|
||||||
(
|
|
||||||
usize,
|
|
||||||
OrgSource<'s>,
|
|
||||||
Option<(OrgSource<'s>, OrgSource<'s>)>,
|
|
||||||
Vec<Object<'s>>,
|
|
||||||
Vec<&'s str>,
|
|
||||||
),
|
|
||||||
> {
|
|
||||||
let parser_context =
|
let parser_context =
|
||||||
context.with_additional_node(ContextElement::ExitMatcherNode(ExitMatcherNode {
|
context.with_additional_node(ContextElement::ExitMatcherNode(ExitMatcherNode {
|
||||||
class: ExitClass::Document,
|
class: ExitClass::Document,
|
||||||
exit_matcher: &headline_title_end,
|
exit_matcher: &headline_end,
|
||||||
}));
|
}));
|
||||||
let standard_set_object_matcher = parser_with_context!(standard_set_object)(&parser_context);
|
let standard_set_object_matcher = parser_with_context!(standard_set_object)(&parser_context);
|
||||||
|
|
||||||
let (
|
let (remaining, (_sol, star_count, ws, title, _line_ending)) = tuple((
|
||||||
remaining,
|
|
||||||
(_sol, star_count, ws, maybe_todo_keyword, title, maybe_tags, _ws, _line_ending),
|
|
||||||
) = tuple((
|
|
||||||
start_of_line,
|
start_of_line,
|
||||||
many1_count(tag("*")),
|
many1_count(tag("*")),
|
||||||
space1,
|
space1,
|
||||||
opt(tuple((heading_keyword, space1))),
|
|
||||||
many1(standard_set_object_matcher),
|
many1(standard_set_object_matcher),
|
||||||
opt(tuple((space0, tags))),
|
|
||||||
space0,
|
|
||||||
alt((line_ending, eof)),
|
alt((line_ending, eof)),
|
||||||
))(input)?;
|
))(input)?;
|
||||||
Ok((
|
Ok((remaining, (star_count, ws, title)))
|
||||||
remaining,
|
|
||||||
(
|
|
||||||
star_count,
|
|
||||||
ws,
|
|
||||||
maybe_todo_keyword,
|
|
||||||
title,
|
|
||||||
maybe_tags
|
|
||||||
.map(|(_ws, tags)| {
|
|
||||||
tags.into_iter()
|
|
||||||
.map(|single_tag| Into::<&str>::into(single_tag))
|
|
||||||
.collect()
|
|
||||||
})
|
|
||||||
.unwrap_or(Vec::new()),
|
|
||||||
),
|
|
||||||
))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
|
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
|
||||||
fn headline_title_end<'r, 's>(
|
fn headline_end<'r, 's>(
|
||||||
_context: Context<'r, 's>,
|
_context: Context<'r, 's>,
|
||||||
input: OrgSource<'s>,
|
input: OrgSource<'s>,
|
||||||
) -> Res<OrgSource<'s>, OrgSource<'s>> {
|
) -> Res<OrgSource<'s>, OrgSource<'s>> {
|
||||||
recognize(tuple((
|
line_ending(input)
|
||||||
opt(tuple((space0, tags, space0))),
|
|
||||||
alt((line_ending, eof)),
|
|
||||||
)))(input)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
|
|
||||||
fn tags<'s>(input: OrgSource<'s>) -> Res<OrgSource<'s>, Vec<OrgSource<'s>>> {
|
|
||||||
let (remaining, (_open, tags, _close)) =
|
|
||||||
tuple((tag(":"), separated_list1(tag(":"), single_tag), tag(":")))(input)?;
|
|
||||||
Ok((remaining, tags))
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
|
|
||||||
fn single_tag<'r, 's>(input: OrgSource<'s>) -> Res<OrgSource<'s>, OrgSource<'s>> {
|
|
||||||
recognize(many1(verify(anychar, |c| {
|
|
||||||
c.is_alphanumeric() || "_@#%".contains(*c)
|
|
||||||
})))(input)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
|
|
||||||
fn heading_keyword<'s>(input: OrgSource<'s>) -> Res<OrgSource<'s>, OrgSource<'s>> {
|
|
||||||
// TODO: This should take into account the value of "#+TODO:" ref https://orgmode.org/manual/Per_002dfile-keywords.html and possibly the configurable variable org-todo-keywords ref https://orgmode.org/manual/Workflow-states.html. Case is significant.
|
|
||||||
alt((tag("TODO"), tag("DONE")))(input)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'s> Document<'s> {
|
impl<'s> Document<'s> {
|
||||||
|
|||||||
@@ -45,11 +45,7 @@ fn name<'r, 's>(
|
|||||||
// TODO: This should be defined by org-entities and optionally org-entities-user
|
// TODO: This should be defined by org-entities and optionally org-entities-user
|
||||||
|
|
||||||
// TODO: Add the rest of the entities, this is a very incomplete list
|
// TODO: Add the rest of the entities, this is a very incomplete list
|
||||||
let (remaining, proto) = alt((alt((
|
let (remaining, proto) = alt((alt((tag_no_case("delta"), tag_no_case("pi"))),))(input)?;
|
||||||
tag_no_case("delta"),
|
|
||||||
tag_no_case("pi"),
|
|
||||||
tag_no_case("ast"),
|
|
||||||
)),))(input)?;
|
|
||||||
Ok((remaining, proto))
|
Ok((remaining, proto))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -189,36 +189,3 @@ pub fn regular_link_description_object_set<'r, 's>(
|
|||||||
parser_with_context!(minimal_set_object)(context),
|
parser_with_context!(minimal_set_object)(context),
|
||||||
))(input)
|
))(input)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
|
|
||||||
pub fn table_cell_set_object<'r, 's>(
|
|
||||||
context: Context<'r, 's>,
|
|
||||||
input: OrgSource<'s>,
|
|
||||||
) -> Res<OrgSource<'s>, Object<'s>> {
|
|
||||||
alt((
|
|
||||||
map(parser_with_context!(citation)(context), Object::Citation),
|
|
||||||
map(
|
|
||||||
parser_with_context!(export_snippet)(context),
|
|
||||||
Object::ExportSnippet,
|
|
||||||
),
|
|
||||||
map(
|
|
||||||
parser_with_context!(footnote_reference)(context),
|
|
||||||
Object::FootnoteReference,
|
|
||||||
),
|
|
||||||
map(parser_with_context!(radio_link)(context), Object::RadioLink),
|
|
||||||
map(
|
|
||||||
parser_with_context!(regular_link)(context),
|
|
||||||
Object::RegularLink,
|
|
||||||
),
|
|
||||||
map(parser_with_context!(plain_link)(context), Object::PlainLink),
|
|
||||||
map(parser_with_context!(angle_link)(context), Object::AngleLink),
|
|
||||||
map(parser_with_context!(org_macro)(context), Object::OrgMacro),
|
|
||||||
map(
|
|
||||||
parser_with_context!(radio_target)(context),
|
|
||||||
Object::RadioTarget,
|
|
||||||
),
|
|
||||||
map(parser_with_context!(target)(context), Object::Target),
|
|
||||||
map(parser_with_context!(timestamp)(context), Object::Timestamp),
|
|
||||||
parser_with_context!(minimal_set_object)(context),
|
|
||||||
))(input)
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -37,13 +37,9 @@ pub fn detect_plain_list<'r, 's>(
|
|||||||
_context: Context<'r, 's>,
|
_context: Context<'r, 's>,
|
||||||
input: OrgSource<'s>,
|
input: OrgSource<'s>,
|
||||||
) -> Res<OrgSource<'s>, ()> {
|
) -> Res<OrgSource<'s>, ()> {
|
||||||
|
// TODO: Add support for plain list items that do not have content on the first line.
|
||||||
if verify(
|
if verify(
|
||||||
tuple((
|
tuple((start_of_line, space0, bullet, space1)),
|
||||||
start_of_line,
|
|
||||||
space0,
|
|
||||||
bullet,
|
|
||||||
alt((space1, line_ending, eof)),
|
|
||||||
)),
|
|
||||||
|(_start, indent, bull, _after_whitespace)| {
|
|(_start, indent, bull, _after_whitespace)| {
|
||||||
Into::<&str>::into(bull) != "*" || indent.len() > 0
|
Into::<&str>::into(bull) != "*" || indent.len() > 0
|
||||||
},
|
},
|
||||||
@@ -139,7 +135,8 @@ pub fn plain_list_item<'r, 's>(
|
|||||||
})(remaining)?;
|
})(remaining)?;
|
||||||
|
|
||||||
// TODO: This isn't taking into account items that immediately line break and then have contents
|
// TODO: This isn't taking into account items that immediately line break and then have contents
|
||||||
let maybe_contentless_item: Res<OrgSource<'_>, OrgSource<'_>> = eof(remaining);
|
let maybe_contentless_item: Res<OrgSource<'_>, OrgSource<'_>> =
|
||||||
|
alt((eof, line_ending))(remaining);
|
||||||
match maybe_contentless_item {
|
match maybe_contentless_item {
|
||||||
Ok((rem, _ws)) => {
|
Ok((rem, _ws)) => {
|
||||||
let source = get_consumed(input, rem);
|
let source = get_consumed(input, rem);
|
||||||
@@ -156,7 +153,7 @@ pub fn plain_list_item<'r, 's>(
|
|||||||
Err(_) => {}
|
Err(_) => {}
|
||||||
};
|
};
|
||||||
|
|
||||||
let (remaining, _ws) = alt((space1, line_ending))(remaining)?;
|
let (remaining, _ws) = space1(remaining)?;
|
||||||
let exit_matcher = plain_list_item_end(indent_level);
|
let exit_matcher = plain_list_item_end(indent_level);
|
||||||
let parser_context = context
|
let parser_context = context
|
||||||
.with_additional_node(ContextElement::ConsumeTrailingWhitespace(true))
|
.with_additional_node(ContextElement::ConsumeTrailingWhitespace(true))
|
||||||
@@ -165,9 +162,12 @@ pub fn plain_list_item<'r, 's>(
|
|||||||
exit_matcher: &exit_matcher,
|
exit_matcher: &exit_matcher,
|
||||||
}));
|
}));
|
||||||
|
|
||||||
let (remaining, (children, _exit_contents)) = many_till(
|
let (remaining, (children, _exit_contents)) = verify(
|
||||||
|
many_till(
|
||||||
parser_with_context!(element(true))(&parser_context),
|
parser_with_context!(element(true))(&parser_context),
|
||||||
parser_with_context!(exit_matcher_parser)(&parser_context),
|
parser_with_context!(exit_matcher_parser)(&parser_context),
|
||||||
|
),
|
||||||
|
|(children, _exit_contents)| !children.is_empty(),
|
||||||
)(remaining)?;
|
)(remaining)?;
|
||||||
|
|
||||||
let (remaining, _trailing_ws) =
|
let (remaining, _trailing_ws) =
|
||||||
@@ -419,40 +419,4 @@ dolar"#,
|
|||||||
"#
|
"#
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn detect_line_break() {
|
|
||||||
let input = OrgSource::new(
|
|
||||||
r#"+
|
|
||||||
"#,
|
|
||||||
);
|
|
||||||
let initial_context: ContextTree<'_, '_> = ContextTree::new();
|
|
||||||
let result = detect_plain_list(&initial_context, input);
|
|
||||||
assert!(result.is_ok());
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn detect_eof() {
|
|
||||||
let input = OrgSource::new(r#"+"#);
|
|
||||||
let initial_context: ContextTree<'_, '_> = ContextTree::new();
|
|
||||||
let result = detect_plain_list(&initial_context, input);
|
|
||||||
assert!(result.is_ok());
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn detect_no_gap() {
|
|
||||||
let input = OrgSource::new(r#"+foo"#);
|
|
||||||
let initial_context: ContextTree<'_, '_> = ContextTree::new();
|
|
||||||
let result = detect_plain_list(&initial_context, input);
|
|
||||||
// Since there is no whitespace after the '+' this is a paragraph, not a plain list.
|
|
||||||
assert!(result.is_err());
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn detect_with_gap() {
|
|
||||||
let input = OrgSource::new(r#"+ foo"#);
|
|
||||||
let initial_context: ContextTree<'_, '_> = ContextTree::new();
|
|
||||||
let result = detect_plain_list(&initial_context, input);
|
|
||||||
assert!(result.is_ok());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -35,6 +35,44 @@ pub struct TextWithProperties<'s> {
|
|||||||
pub properties: Vec<Token<'s>>,
|
pub properties: Vec<Token<'s>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<'s> TextWithProperties<'s> {
|
||||||
|
pub fn unquote(&self) -> Result<String, Box<dyn std::error::Error>> {
|
||||||
|
let mut out = String::with_capacity(self.text.len());
|
||||||
|
if !self.text.starts_with(r#"""#) {
|
||||||
|
return Err("Quoted text does not start with quote.".into());
|
||||||
|
}
|
||||||
|
if !self.text.ends_with(r#"""#) {
|
||||||
|
return Err("Quoted text does not end with quote.".into());
|
||||||
|
}
|
||||||
|
let interior_text = &self.text[1..(self.text.len() - 1)];
|
||||||
|
let mut state = ParseState::Normal;
|
||||||
|
for current_char in interior_text.chars().into_iter() {
|
||||||
|
state = match (state, current_char) {
|
||||||
|
(ParseState::Normal, '\\') => ParseState::Escape,
|
||||||
|
(ParseState::Normal, _) => {
|
||||||
|
out.push(current_char);
|
||||||
|
ParseState::Normal
|
||||||
|
}
|
||||||
|
(ParseState::Escape, 'n') => {
|
||||||
|
out.push('\n');
|
||||||
|
ParseState::Normal
|
||||||
|
}
|
||||||
|
(ParseState::Escape, '\\') => {
|
||||||
|
out.push('\\');
|
||||||
|
ParseState::Normal
|
||||||
|
}
|
||||||
|
(ParseState::Escape, '"') => {
|
||||||
|
out.push('"');
|
||||||
|
ParseState::Normal
|
||||||
|
}
|
||||||
|
_ => todo!(),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(out)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
enum ParseState {
|
enum ParseState {
|
||||||
Normal,
|
Normal,
|
||||||
Escape,
|
Escape,
|
||||||
@@ -95,42 +133,6 @@ impl<'s> Token<'s> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn unquote(text: &str) -> Result<String, Box<dyn std::error::Error>> {
|
|
||||||
let mut out = String::with_capacity(text.len());
|
|
||||||
if !text.starts_with(r#"""#) {
|
|
||||||
return Err("Quoted text does not start with quote.".into());
|
|
||||||
}
|
|
||||||
if !text.ends_with(r#"""#) {
|
|
||||||
return Err("Quoted text does not end with quote.".into());
|
|
||||||
}
|
|
||||||
let interior_text = &text[1..(text.len() - 1)];
|
|
||||||
let mut state = ParseState::Normal;
|
|
||||||
for current_char in interior_text.chars().into_iter() {
|
|
||||||
state = match (state, current_char) {
|
|
||||||
(ParseState::Normal, '\\') => ParseState::Escape,
|
|
||||||
(ParseState::Normal, _) => {
|
|
||||||
out.push(current_char);
|
|
||||||
ParseState::Normal
|
|
||||||
}
|
|
||||||
(ParseState::Escape, 'n') => {
|
|
||||||
out.push('\n');
|
|
||||||
ParseState::Normal
|
|
||||||
}
|
|
||||||
(ParseState::Escape, '\\') => {
|
|
||||||
out.push('\\');
|
|
||||||
ParseState::Normal
|
|
||||||
}
|
|
||||||
(ParseState::Escape, '"') => {
|
|
||||||
out.push('"');
|
|
||||||
ParseState::Normal
|
|
||||||
}
|
|
||||||
_ => todo!(),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(out)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
|
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
|
||||||
pub fn sexp_with_padding<'s>(input: &'s str) -> Res<&'s str, Token<'s>> {
|
pub fn sexp_with_padding<'s>(input: &'s str) -> Res<&'s str, Token<'s>> {
|
||||||
let (remaining, _) = multispace0(input)?;
|
let (remaining, _) = multispace0(input)?;
|
||||||
|
|||||||
@@ -12,13 +12,14 @@ use nom::multi::many1;
|
|||||||
use nom::multi::many_till;
|
use nom::multi::many_till;
|
||||||
use nom::sequence::tuple;
|
use nom::sequence::tuple;
|
||||||
|
|
||||||
use super::object_parser::table_cell_set_object;
|
|
||||||
use super::org_source::OrgSource;
|
use super::org_source::OrgSource;
|
||||||
use super::Context;
|
use super::Context;
|
||||||
use crate::error::Res;
|
use crate::error::Res;
|
||||||
use crate::parser::exiting::ExitClass;
|
use crate::parser::exiting::ExitClass;
|
||||||
use crate::parser::greater_element::TableRow;
|
use crate::parser::greater_element::TableRow;
|
||||||
use crate::parser::lesser_element::TableCell;
|
use crate::parser::lesser_element::TableCell;
|
||||||
|
use crate::parser::object::Object;
|
||||||
|
use crate::parser::object_parser::minimal_set_object;
|
||||||
use crate::parser::parser_context::ContextElement;
|
use crate::parser::parser_context::ContextElement;
|
||||||
use crate::parser::parser_context::ExitMatcherNode;
|
use crate::parser::parser_context::ExitMatcherNode;
|
||||||
use crate::parser::parser_with_context::parser_with_context;
|
use crate::parser::parser_with_context::parser_with_context;
|
||||||
@@ -161,3 +162,14 @@ fn org_mode_table_cell_end<'r, 's>(
|
|||||||
) -> Res<OrgSource<'s>, OrgSource<'s>> {
|
) -> Res<OrgSource<'s>, OrgSource<'s>> {
|
||||||
recognize(tuple((space0, alt((tag("|"), peek(line_ending))))))(input)
|
recognize(tuple((space0, alt((tag("|"), peek(line_ending))))))(input)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
|
||||||
|
pub fn table_cell_set_object<'r, 's>(
|
||||||
|
context: Context<'r, 's>,
|
||||||
|
input: OrgSource<'s>,
|
||||||
|
) -> Res<OrgSource<'s>, Object<'s>> {
|
||||||
|
not(|i| context.check_exit_matcher(i))(input)?;
|
||||||
|
|
||||||
|
parser_with_context!(minimal_set_object)(context)(input)
|
||||||
|
// TODO: add citations, export snippets, footnote references, links, macros, radio targets, targets, and timestamps.
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user