diff --git a/.justfile b/.justfile
index f9981d9..6519990 100644
--- a/.justfile
+++ b/.justfile
@@ -24,7 +24,7 @@ alias w := run-watch
[private]
quick-assess:
- {{ just_cmd }} test lint check
+ {{ just_cmd }} lint check quick-test-cover
[private]
quick-assess-run:
diff --git a/src/dev.rs b/src/dev.rs
index a9f8fbf..ffdf2ac 100644
--- a/src/dev.rs
+++ b/src/dev.rs
@@ -4,7 +4,7 @@ pub fn elog(function: &str, message: &str) {
}
// Paths in this slice suppress logging if found in the stack trace
-pub const SKIP_PATHS: &[&str] = &["en::types::Config::parse_text"];
+pub const SKIP_PATHS: &[&str] = &["en::types::Graph::parse"];
#[macro_export]
macro_rules! log {
diff --git a/src/router/handlers/graph.rs b/src/router/handlers/graph.rs
index a0b3589..3079a8e 100644
--- a/src/router/handlers/graph.rs
+++ b/src/router/handlers/graph.rs
@@ -8,7 +8,7 @@ use crate::{syntax::serial::populate_graph, router::handlers, types::Node};
pub async fn node(Path(id): Path) -> Response {
let graph = populate_graph();
let result = graph.find_node(&id);
- let nodes: Vec = graph.nodes.into_values().collect();
+ let nodes: Vec = graph.nodes.clone().into_values().collect();
let not_found = result.node.is_none();
let node = result
.node
@@ -29,7 +29,7 @@ pub async fn node(Path(id): Path) -> Response {
let mut context = tera::Context::default();
context.insert("node", &node);
context.insert("nodes", &nodes);
- context.insert("text", &content::parse(&node.text, &graph.meta.config));
+ context.insert("text", &content::parse(&node.text, &graph));
context.insert("incoming", &graph.incoming.get(&id));
context.insert("config", &graph.meta.config);
diff --git a/src/router/handlers/template.rs b/src/router/handlers/template.rs
index 270449f..cc4cb7a 100644
--- a/src/router/handlers/template.rs
+++ b/src/router/handlers/template.rs
@@ -156,10 +156,8 @@ mod tests {
let node = crate::types::Node::new(Some(payload.to_string()));
let graph = crate::syntax::serial::populate_graph();
context.insert("node", &node);
- context.insert(
- "text",
- &crate::syntax::content::parse(&node.text, &graph.meta.config),
- );
+ context
+ .insert("text", &crate::syntax::content::parse(&node.text, &graph));
context.insert("incoming", &graph.incoming.get(&node.id));
context.insert("config", &graph.meta.config);
let (body, status) = render("node.html", &context, None);
diff --git a/src/syntax/content.rs b/src/syntax/content.rs
index 4e7a4a7..b802efa 100644
--- a/src/syntax/content.rs
+++ b/src/syntax/content.rs
@@ -1,6 +1,6 @@
use parser::{token::Token, lexeme::Lexeme};
-use crate::types::Config;
+use crate::types::Graph;
pub mod parser;
@@ -8,12 +8,13 @@ pub trait Parseable: std::fmt::Display {
fn probe(lexeme: &Lexeme) -> bool;
fn lex(lexeme: &Lexeme) -> Self;
fn render(&self) -> String;
+ fn flatten(&self) -> String;
}
type Probe = fn(&Lexeme) -> bool;
type Lexer = fn(&Lexeme) -> Token;
type LexMap<'lm> = &'lm [(Probe, Lexer)];
-pub fn parse(text: &str, config: &Config) -> String {
- parser::read(text, config)
+pub fn parse(text: &str, graph: &Graph) -> String {
+ parser::read(text, graph)
}
diff --git a/src/syntax/content/parser.rs b/src/syntax/content/parser.rs
index 18ee600..f00de4f 100644
--- a/src/syntax/content/parser.rs
+++ b/src/syntax/content/parser.rs
@@ -1,4 +1,4 @@
-use crate::{prelude::*, types::Config};
+use crate::{prelude::*, types::Graph};
use super::{Parseable as _, Token, LexMap};
use token::{linebreak::LineBreak, literal::Literal};
use lexeme::Lexeme;
@@ -20,7 +20,7 @@ const LEXMAP: LexMap = &[
}),
];
-fn lex(text: &str, map: LexMap, config: &Config, blocking: bool) -> Vec {
+fn lex(text: &str, map: LexMap, graph: &Graph, blocking: bool) -> Vec {
let mut tokens: Vec = Vec::default();
let mut state = state::State::default();
@@ -44,7 +44,7 @@ fn lex(text: &str, map: LexMap, config: &Config, blocking: bool) -> Vec {
&mut state,
&mut tokens,
&mut iterator,
- config,
+ graph,
) {
continue;
}
@@ -59,6 +59,7 @@ fn lex(text: &str, map: LexMap, config: &Config, blocking: bool) -> Vec {
&mut state,
&mut tokens,
&mut iterator,
+ graph,
) {
continue;
}
@@ -77,20 +78,28 @@ fn lex(text: &str, map: LexMap, config: &Config, blocking: bool) -> Vec {
tokens
}
+pub(super) fn read(input: &str, graph: &Graph) -> String {
+ parse(&lex(input, LEXMAP, graph, true))
+}
+
+/// Apply end-to-end point and inline parsing for nested formatting, such as
+/// inside the display text of anchors and list items
+pub fn nest(input: &str, graph: &Graph) -> String {
+ parse(&lex(input, LEXMAP, graph, false))
+}
+
+// Strip special syntax for display in noninteractive or plain-text display
+pub fn flatten(input: &str, graph: &Graph) -> String {
+ let tokens = lex(input, LEXMAP, graph, true);
+ let flat = tokens.iter().map(Token::flatten).collect::();
+ log!("Flattened {tokens:?} to {flat}");
+ flat
+}
+
fn parse(tokens: &[Token]) -> String {
tokens.iter().map(Token::render).collect::()
}
-/// Apply end-to-end point and inline parsing for nested contexts, such as
-/// inside the displayed text of other tokens like anchors and list items
-pub fn nest(input: &str, config: &Config) -> String {
- parse(&lex(input, LEXMAP, config, false))
-}
-
-pub(super) fn read(input: &str, config: &Config) -> String {
- parse(&lex(input, LEXMAP, config, true))
-}
-
#[cfg(test)]
mod tests {
use crate::{
@@ -101,7 +110,7 @@ mod tests {
use super::*;
fn read_noconfig(input: &str) -> String {
- read(input, &Graph::new(None).meta.config)
+ read(input, &Graph::default())
}
#[test]
@@ -112,7 +121,7 @@ mod tests {
#[test]
fn mixed_sample() {
let en = "`this |test|` tries ## to |brea|k|: things";
- let html = r#"this |test| tries ## to brea : things
"#;
+ let html = r#"this |test| tries ## to brea : things
"#;
assert_eq!(read_noconfig(en), html);
}
diff --git a/src/syntax/content/parser/context/anchor.rs b/src/syntax/content/parser/context/anchor.rs
index 8843ab4..665627c 100644
--- a/src/syntax/content/parser/context/anchor.rs
+++ b/src/syntax/content/parser/context/anchor.rs
@@ -1,8 +1,9 @@
use crate::{
prelude::*,
syntax::content::parser::{
- state::State, context::Inline, lexeme::Lexeme, token::Token,
+ context::Inline, lexeme::Lexeme, state::State, token::Token,
},
+ types::Graph,
};
/// Handles open anchor contexts until an anchor token is fully parsed.
@@ -16,6 +17,7 @@ pub fn parse(
lexeme: &Lexeme,
state: &mut State,
tokens: &mut Vec,
+ graph: &Graph,
) -> bool {
log!("Solving: {}", state.clone().buffers.anchor);
let buffer = &mut state.buffers.anchor;
@@ -61,19 +63,16 @@ pub fn parse(
candidate.set_destination(Some(&candidate.text().clone()));
candidate.text_push("s");
if lexeme.last() {
- tokens.push(Token::Anchor(candidate.clone()));
- state.context.inline = Inline::None;
+ push(None, tokens, state, graph);
}
return true;
} else if lexeme.match_char('|') && lexeme.is_next_delimiter() {
log!("End: Pipe followed by delimiter");
if buffer.destination.is_empty() {
- candidate.set_destination(Some(&candidate.text().clone()));
+ push(Some(&candidate.text().clone()), tokens, state, graph);
} else {
- candidate.set_destination(Some(&buffer.destination.clone()));
+ push(Some(&buffer.destination.clone()), tokens, state, graph);
}
- tokens.push(Token::Anchor(candidate.clone()));
- state.context.inline = Inline::None;
return true;
} else if lexeme.match_char('|') && !candidate.balanced() {
log!("State: Found a pipe, but no boundary: destination follows");
@@ -90,23 +89,17 @@ pub fn parse(
return true;
} else if !candidate.external() && lexeme.is_delimiter() {
log!("End: Internal anchor trailed by delimiter");
- candidate.set_destination(Some(&buffer.destination.clone()));
- tokens.push(Token::Anchor(candidate.clone()));
- state.context.inline = Inline::None;
+ push(Some(&buffer.destination.clone()), tokens, state, graph);
return false;
} else if lexeme.is_next_whitespace() {
log!("End: next is whitespace");
buffer.destination.push_str(&lexeme.text());
- candidate.set_destination(Some(&buffer.destination.clone()));
- tokens.push(Token::Anchor(candidate.clone()));
- state.context.inline = Inline::None;
+ push(Some(&buffer.destination.clone()), tokens, state, graph);
return true;
} else if lexeme.last() {
log!("End: end of input");
buffer.destination.push_str(&lexeme.text());
- candidate.set_destination(Some(&buffer.destination.clone()));
- tokens.push(Token::Anchor(candidate.clone()));
- state.context.inline = Inline::None;
+ push(Some(&buffer.destination.clone()), tokens, state, graph);
return true;
// This else branch is the 'no end found yet' state and will keep
@@ -119,9 +112,7 @@ pub fn parse(
);
buffer.destination.push_str(&lexeme.text());
if lexeme.last() {
- candidate.set_destination(Some(&buffer.destination.clone()));
- tokens.push(Token::Anchor(candidate.clone()));
- state.context.inline = Inline::None;
+ push(Some(&buffer.destination.clone()), tokens, state, graph);
}
return true;
}
@@ -136,47 +127,79 @@ pub fn parse(
"Anchor context parsing done but no destination found: {:#?}",
state.buffers.anchor
);
- tokens.push(Token::Anchor(candidate.clone()));
- state.context.inline = Inline::None;
+ push(None, tokens, state, graph);
false
}
+fn push(
+ d: Option<&str>,
+ tokens: &mut Vec,
+ state: &mut State,
+ graph: &Graph,
+) {
+ let candidate = &mut state.buffers.anchor.candidate;
+ if d.is_some() {
+ candidate.set_destination(d);
+ }
+
+ if let Some(node_id) = candidate.node_id()
+ && let Some(node) = graph.find_node(&node_id).node
+ {
+ log!("Found matching node {node:?} for anchor candidate {candidate}");
+ candidate.set_node(&node);
+ } else {
+ log!("Could not find a matching node for anchor candidate {candidate}");
+ }
+
+ tokens.push(Token::Anchor(Box::new(candidate.clone())));
+ state.context.inline = Inline::None;
+}
+
#[cfg(test)]
mod tests {
use crate::{syntax::content::parser, types::Graph};
fn read(input: &str) -> String {
- parser::read(input, &Graph::new(None).meta.config)
+ parser::read(input, &Graph::default())
}
#[test]
fn flanking() {
- assert_eq!(read("|Node|"), r#"Node
"#);
+ assert_eq!(
+ read("|Node|"),
+ r#"Node
"#
+ );
}
#[test]
fn flanking_with_trailing_comma() {
- assert_eq!(read("|Node|,"), r#"Node ,
"#);
+ assert_eq!(
+ read("|Node|,"),
+ r#"Node ,
"#
+ );
}
#[test]
fn flanking_with_trailing_comma_and_space() {
assert_eq!(
read("|Node|, at"),
- r#"Node , at
"#
+ r#"Node , at
"#
);
}
#[test]
fn flanking_at_eoi() {
- assert_eq!(read("|Node|"), r#"Node
"#);
+ assert_eq!(
+ read("|Node|"),
+ r#"Node
"#
+ );
}
#[test]
fn needless_three_pipe_anchor() {
assert_eq!(
read("|Node|Destination|"),
- r#"Node
"#
+ r#"Node
"#
);
}
@@ -184,7 +207,7 @@ mod tests {
fn nonleading_second_pipe() {
assert_eq!(
read("Go to Node|Destination|, here"),
- r#"Go to Node , here
"#,
+ r#"Go to Node , here
"#,
);
}
@@ -192,7 +215,7 @@ mod tests {
fn anchor_to_node_s() {
assert_eq!(
read("The |letter s|s|'s node: |s|!"),
- r#"The letter s 's node: s !
"#
+ r#"The letter s 's node: s !
"#
);
}
@@ -200,7 +223,7 @@ mod tests {
fn nonleading_plural_anchor() {
assert_eq!(
read("The flower|s bloomed"),
- r#"The flowers bloomed
"#
+ r#"The flowers bloomed
"#
);
}
@@ -208,7 +231,7 @@ mod tests {
fn leading_plural_anchor() {
assert_eq!(
read("Interfaces are |element|s of |system|s."),
- r#"Interfaces are elements of systems .
"#
+ r#"Interfaces are elements of systems .
"#
);
}
@@ -216,7 +239,7 @@ mod tests {
fn leading_multiword_anchor() {
assert_eq!(
read("interactions are |basic elements| of systems"),
- r#"interactions are basic elements of systems
"#
+ r#"interactions are basic elements of systems
"#
);
}
@@ -224,7 +247,7 @@ mod tests {
fn explicit_end_of_destination() {
assert_eq!(
read("interactions are |basic elements|BasicElements| of systems"),
- r#"interactions are basic elements of systems
"#
+ r#"interactions are basic elements of systems
"#
);
}
@@ -232,20 +255,23 @@ mod tests {
fn explicit_end_of_external_destination() {
assert_eq!(
read("this |anchor example|https://example.com| is external"),
- r#"this anchor example is external
"#
+ r#"this anchor example is external
"#
);
}
#[test]
fn anchor_destination_at_eoi() {
- assert_eq!(read("a |b c|d"), r#"a b c
"#);
+ assert_eq!(
+ read("a |b c|d"),
+ r#"a b c
"#
+ );
}
#[test]
fn external_anchor_destination_at_eoi() {
assert_eq!(
read("a b|https://example.com"),
- r#"a b
"#
+ r#"a b
"#
);
}
@@ -253,7 +279,7 @@ mod tests {
fn nonleading_plural_anchor_at_eoi() {
assert_eq!(
read("element|s"),
- r#"elements
"#
+ r#"elements
"#
);
}
@@ -261,7 +287,7 @@ mod tests {
fn leading_plural_anchor_at_eoi() {
assert_eq!(
read("|element|s"),
- r#"elements
"#
+ r#"elements
"#
);
}
@@ -271,7 +297,7 @@ mod tests {
read(
"a |false dichotomy|https://en.wikipedia.org/wiki/False_dilemma|."
),
- r#"a false dichotomy .
"#
+ r#"a false dichotomy .
"#
);
}
@@ -284,7 +310,7 @@ mod tests {
"at rustup.rs",
)),
concat!(
- r#"Rust toolchain "#,
+ r#"
Rust toolchain "#,
"\n",
"at rustup.rs
",
)
@@ -295,7 +321,7 @@ mod tests {
fn http_external_anchor_leading_no_third_then_space() {
assert_eq!(
read("|Rust toolchain|https://rustup.rs/ at rustup.rs"),
- r#"Rust toolchain at rustup.rs
"#
+ r#"Rust toolchain at rustup.rs
"#
);
}
@@ -303,7 +329,7 @@ mod tests {
fn http_external_anchor_leading_no_third_then_eoi() {
assert_eq!(
read("|Rust toolchain|https://rustup.rs/"),
- r#"Rust toolchain
"#
+ r#"Rust toolchain
"#
);
}
@@ -313,7 +339,7 @@ mod tests {
read("\n|SomeAnchor|\n"),
concat!(
"\n",
- r#"SomeAnchor
"#
+ r#"SomeAnchor
"#
),
);
}
@@ -323,9 +349,9 @@ mod tests {
assert_eq!(
read("|SomeAnchor|\n|SomeOtherAnchor|\n"),
concat!(
- r#"SomeAnchor "#,
+ r#"
SomeAnchor "#,
"\n",
- r#"SomeOtherAnchor
"#
+ r#"SomeOtherAnchor
"#
)
);
}
@@ -335,10 +361,10 @@ mod tests {
assert_eq!(
read("|SomeAnchor|\n\n|SomeOtherAnchor|\n"),
concat!(
- r#"SomeAnchor
"#,
+ r#"SomeAnchor
"#,
"\n",
"\n",
- r#"SomeOtherAnchor
"#
+ r#"SomeOtherAnchor
"#
),
);
}
@@ -347,7 +373,7 @@ mod tests {
fn trailing_anchor() {
assert_eq!(
read("see acks|acks"),
- r#"see acks
"#
+ r#"see acks
"#
);
}
@@ -355,7 +381,10 @@ mod tests {
fn trailing_anchor_with_newline() {
assert_eq!(
read("\nsee acks|acks\n"),
- concat!("\n", r#"see acks
"#)
+ concat!(
+ "\n",
+ r#"see acks
"#
+ )
);
}
@@ -383,7 +412,7 @@ mod tests {
fn anchor_with_trailing_single_quote() {
assert_eq!(
read("the |lion|'s mouth"),
- r#"the lion 's mouth
"#,
+ r#"the lion 's mouth
"#,
);
}
@@ -391,7 +420,7 @@ mod tests {
fn anchor_with_trailing_double_quote() {
assert_eq!(
read(r#"the "|real|" motive"#),
- r#"the "real " motive
"#,
+ r#"the "real " motive
"#,
);
}
@@ -399,7 +428,7 @@ mod tests {
fn anchor_with_trailing_parenthesis() {
assert_eq!(
read("this (though |true|) was questioned"),
- r#"this (though true ) was questioned
"#,
+ r#"this (though true ) was questioned
"#,
);
}
@@ -407,7 +436,7 @@ mod tests {
fn anchor_with_leading_single_quote() {
assert_eq!(
read("the 'real|Reality' motive"),
- r#"the 'real ' motive
"#,
+ r#"the 'real ' motive
"#,
);
}
@@ -415,7 +444,7 @@ mod tests {
fn anchor_with_leading_double_quote() {
assert_eq!(
read(r#"the "real|Reality" motive"#),
- r#"the "real " motive
"#,
+ r#"the "real " motive
"#,
);
}
@@ -423,7 +452,7 @@ mod tests {
fn anchor_with_leading_parenthesis() {
assert_eq!(
read("her (last|Surname) name"),
- r#"her (last ) name
"#,
+ r#"her (last ) name
"#,
);
}
@@ -431,7 +460,7 @@ mod tests {
fn anchor_with_internal_apostrophe() {
assert_eq!(
read("the |lion's mouth|album was released"),
- r#"the lion's mouth was released
"#
+ r#"the lion's mouth was released
"#
);
}
@@ -439,7 +468,7 @@ mod tests {
fn nonleading_anchor_with_internal_apostrophe() {
assert_eq!(
read("they decided to stay at Jane's|YellowHouse that night"),
- r#"they decided to stay at Jane's that night
"#
+ r#"they decided to stay at Jane's that night
"#
);
}
@@ -447,7 +476,7 @@ mod tests {
fn nonleading_anchor_with_internal_apostrophe_at_eoi() {
assert_eq!(
read("they decided to stay at Jane's|YellowHouse"),
- r#"they decided to stay at Jane's
"#
+ r#"they decided to stay at Jane's
"#
);
}
@@ -455,7 +484,7 @@ mod tests {
fn nonleading_anchor_with_internal_apostrophe_at_soi() {
assert_eq!(
read("Jane's|YellowHouse that night"),
- r#"Jane's that night
"#
+ r#"Jane's that night
"#
);
}
@@ -463,7 +492,7 @@ mod tests {
fn anchor_with_internal_double_quotes() {
assert_eq!(
read(r#"the |"real"|Truth motive"#),
- r#"the "real" motive
"#,
+ r#"the "real" motive
"#,
);
}
@@ -471,7 +500,7 @@ mod tests {
fn anchor_with_internal_double_quotes_wrapping_spaced_words() {
assert_eq!(
read(r#"the |"bare reality"|Ideology they believed"#),
- r#"the "bare reality" they believed
"#,
+ r#"the "bare reality" they believed
"#,
);
}
@@ -479,7 +508,7 @@ mod tests {
fn anchor_with_internal_parenthesis() {
assert_eq!(
read("her |last (name)|Surname was Amad"),
- r#"her last (name) was Amad
"#,
+ r#"her last (name) was Amad
"#,
);
}
@@ -487,7 +516,7 @@ mod tests {
fn anchor_with_internal_parenthesis_wrapping_spaced_words() {
assert_eq!(
read("this |truth (though questionable) was fine|Absurd to them "),
- r#"this truth (though questionable) was fine to them
"#
+ r#"this truth (though questionable) was fine to them
"#
);
}
}
diff --git a/src/syntax/content/parser/context/block.rs b/src/syntax/content/parser/context/block.rs
index e2d278e..4dfede6 100644
--- a/src/syntax/content/parser/context/block.rs
+++ b/src/syntax/content/parser/context/block.rs
@@ -14,7 +14,7 @@ use crate::{
},
},
},
- types::Config,
+ types::Graph,
};
pub fn parse(
@@ -22,7 +22,7 @@ pub fn parse(
state: &mut State,
tokens: &mut Vec,
iterator: &mut Peekable>,
- config: &Config,
+ graph: &Graph,
) -> bool {
match state.context.block {
Block::None => {
@@ -34,7 +34,7 @@ pub fn parse(
} else if Header::probe(lexeme) {
let mut header = Header::lex(lexeme);
header.dom_id = Some(Header::make_id(
- config,
+ &graph.meta.config,
iterator.peek().map_or(&Lexeme::default(), |l| l),
&mut state.dom_ids,
));
@@ -47,7 +47,7 @@ pub fn parse(
state.context.block = Block::List;
state.buffers.list.candidate.ordered = lexeme.match_char('+');
return super::list::parse(
- lexeme, state, tokens, iterator, config,
+ lexeme, state, tokens, iterator, graph,
);
} else if Paragraph::probe(lexeme) {
log!("Block Context: None -> Paragraph on {lexeme}");
@@ -80,7 +80,7 @@ pub fn parse(
}
},
Block::List => {
- return super::list::parse(lexeme, state, tokens, iterator, config);
+ return super::list::parse(lexeme, state, tokens, iterator, graph);
},
}
false
@@ -103,7 +103,7 @@ mod tests {
};
fn read(input: &str) -> String {
- parser::read(input, &Graph::new(None).meta.config)
+ parser::read(input, &Graph::default())
}
#[test]
diff --git a/src/syntax/content/parser/context/inline.rs b/src/syntax/content/parser/context/inline.rs
index 7b05af7..eb339ef 100644
--- a/src/syntax/content/parser/context/inline.rs
+++ b/src/syntax/content/parser/context/inline.rs
@@ -11,6 +11,7 @@ use crate::{
token::{Token, anchor::Anchor, code::Code, literal::Literal},
},
},
+ types::Graph,
};
pub fn parse(
@@ -18,6 +19,7 @@ pub fn parse(
state: &mut State,
tokens: &mut Vec,
iterator: &mut Peekable>,
+ graph: &Graph,
) -> bool {
match state.context.inline {
Inline::None => {
@@ -54,7 +56,7 @@ pub fn parse(
}
},
Inline::Anchor => {
- if context::anchor::parse(lexeme, state, tokens) {
+ if context::anchor::parse(lexeme, state, tokens, graph) {
return true;
}
},
diff --git a/src/syntax/content/parser/context/list.rs b/src/syntax/content/parser/context/list.rs
index 13e4141..bb36915 100644
--- a/src/syntax/content/parser/context/list.rs
+++ b/src/syntax/content/parser/context/list.rs
@@ -9,7 +9,7 @@ use crate::{
state::{ListBuffer, State},
token::{Token, item::Item},
},
- types::Config,
+ types::Graph,
};
/// Handles open list contexts until a list is fully parsed.
@@ -25,7 +25,7 @@ pub fn parse(
state: &mut State,
tokens: &mut Vec,
iterator: &mut Peekable>,
- config: &Config,
+ graph: &Graph,
) -> bool {
let buffer = &mut state.buffers.list;
let candidate = &mut buffer.candidate;
@@ -52,7 +52,7 @@ pub fn parse(
}
if item_candidate.depth.is_some() {
// if the current item candidate has a known depth, push it
- item_candidate.text = nest(&item_candidate.text, config);
+ item_candidate.text = nest(&item_candidate.text, graph);
candidate.items.push(item_candidate.clone());
}
// push list candidate, reset state and exit context
@@ -64,7 +64,7 @@ pub fn parse(
} else if lexeme.match_char('\n') {
// found end of item, push it and reset state
log!("Accepting item candidate {item_candidate}");
- item_candidate.text = nest(&item_candidate.text, config);
+ item_candidate.text = nest(&item_candidate.text, graph);
candidate.items.push(item_candidate.clone());
*item_candidate = Item::default();
buffer.depth = 0;
@@ -89,11 +89,11 @@ mod tests {
syntax::content::parser::{
self, context::list::parse, lexeme::Lexeme, state::State,
},
- types::{Config, Graph},
+ types::Graph,
};
fn read(input: &str) -> String {
- parser::read(input, &Graph::new(None).meta.config)
+ parser::read(input, &Graph::default())
}
#[test]
@@ -273,13 +273,13 @@ mod tests {
fn bad_context() {
let mut state = State::default();
let lexemes = Lexeme::collect(&["a", "b", "c"].map(str::to_string));
- let config = Config::default();
+ let graph = Graph::default();
parse(
&Lexeme::default(),
&mut state,
&mut vec![],
&mut lexemes.iter().peekable(),
- &config,
+ &graph,
);
}
}
diff --git a/src/syntax/content/parser/point.rs b/src/syntax/content/parser/point.rs
index aaf5a11..1dab1dd 100644
--- a/src/syntax/content/parser/point.rs
+++ b/src/syntax/content/parser/point.rs
@@ -65,14 +65,14 @@ mod tests {
use crate::{syntax::content::parser, types::Graph};
fn read(input: &str) -> String {
- parser::read(input, &Graph::new(None).meta.config)
+ parser::read(input, &Graph::default())
}
#[test]
fn oblique_anchor() {
assert_eq!(
read("w _|S|_ w"),
- r#"w S w
"#
+ r#"w S w
"#
);
}
@@ -80,7 +80,7 @@ mod tests {
fn oblique_anchor_with_trailing_comma() {
assert_eq!(
read("w _|S|_, w"),
- r#"w S , w
"#
+ r#"w S , w
"#
);
}
@@ -88,9 +88,9 @@ mod tests {
fn oblique() {
assert_eq!(
read(
- "_|this anchor is oblique|o as are these literals_ but not these _just these_, not this _and these with an |anchor| again_"
+ "_|this anchor is oblique|o as are these literals_ but not these _just these_, not this _and these with an |anc80r| again_"
),
- r#"this anchor is oblique as are these literals but not these just these , not this and these with an anchor again
"#
+ r#"this anchor is oblique as are these literals but not these just these , not this and these with an anc80r again
"#
);
}
diff --git a/src/syntax/content/parser/token.rs b/src/syntax/content/parser/token.rs
index c02f937..a6e9124 100644
--- a/src/syntax/content/parser/token.rs
+++ b/src/syntax/content/parser/token.rs
@@ -18,7 +18,7 @@ pub mod underline;
#[derive(Debug, Eq, PartialEq, Clone)]
pub enum Token {
- Anchor(anchor::Anchor),
+ Anchor(Box),
Bold(bold::Bold),
CheckBox(checkbox::CheckBox),
Code(code::Code),
@@ -53,6 +53,25 @@ impl Token {
Token::Underline(ref d) => d.render(),
}
}
+
+ pub fn flatten(&self) -> String {
+ match *self {
+ Token::Anchor(ref d) => d.flatten(),
+ Token::Bold(ref d) => d.flatten(),
+ Token::CheckBox(ref d) => d.flatten(),
+ Token::Code(ref d) => d.flatten(),
+ Token::Strike(ref d) => d.flatten(),
+ Token::Header(ref d) => d.flatten(),
+ Token::Item(ref d) => d.flatten(),
+ Token::LineBreak(ref d) => d.flatten(),
+ Token::List(ref d) => d.flatten(),
+ Token::Literal(ref d) => d.flatten(),
+ Token::Oblique(ref d) => d.flatten(),
+ Token::Paragraph(ref d) => d.flatten(),
+ Token::PreFormat(ref d) => d.flatten(),
+ Token::Underline(ref d) => d.flatten(),
+ }
+ }
}
impl std::fmt::Display for Token {
diff --git a/src/syntax/content/parser/token/anchor.rs b/src/syntax/content/parser/token/anchor.rs
index 25481dd..4569aee 100644
--- a/src/syntax/content/parser/token/anchor.rs
+++ b/src/syntax/content/parser/token/anchor.rs
@@ -7,6 +7,7 @@ use crate::{
pub struct Anchor {
text: String,
destination: Option,
+ node_id: Option,
node: Option,
leading: bool,
balanced: bool,
@@ -18,6 +19,7 @@ impl Anchor {
text: &str,
destination: &str,
node: Option,
+ node_id: Option,
leading: bool,
external: bool,
balanced: bool,
@@ -26,6 +28,7 @@ impl Anchor {
text: text.to_owned(),
destination: Some(String::from(destination)),
node,
+ node_id,
leading,
external,
balanced,
@@ -76,6 +79,14 @@ impl Anchor {
self.leading = leading;
}
+ pub fn set_node(&mut self, node: &Node) {
+ self.node = Some(node.to_owned());
+ }
+
+ pub fn node_id(&self) -> Option {
+ self.node_id.clone()
+ }
+
fn route(&mut self) {
self.destination = if let Some(destination) = self.destination.clone() {
if destination.contains(":") || destination.contains("/") {
@@ -83,8 +94,10 @@ impl Anchor {
} else if destination.is_empty() && self.text.is_empty() {
None
} else if destination.is_empty() {
+ self.node_id = Some(self.text.clone());
Some(format!("/node/{}", self.text))
} else {
+ self.node_id = self.destination.clone();
Some(format!("/node/{destination}"))
}
} else {
@@ -111,7 +124,30 @@ impl Parseable for Anchor {
)
};
- format!(r#"{} "#, destination, &self.text)
+ let summary = if let Some(node) = self.node.clone() {
+ node.summary
+ } else {
+ String::default()
+ };
+
+ let classes = if self.node.is_some() {
+ String::from(r#"class="attached""#)
+ } else if !self.external {
+ String::from(r#"class="detached""#)
+ } else if self.external {
+ String::from(r#"class="external""#)
+ } else {
+ String::default()
+ };
+
+ format!(
+ r#"{} "#,
+ destination, self.text,
+ )
+ }
+
+ fn flatten(&self) -> String {
+ self.text.clone()
}
}
@@ -167,7 +203,7 @@ mod tests {
anchor.set_destination(Some("AnchorDest"));
assert_eq!(
anchor.render(),
- r#"AnchorText "#
+ r#"AnchorText "#
);
}
@@ -190,20 +226,20 @@ mod tests {
fn token_display() {
let mut anchor = Anchor::default();
assert_eq!(
- format!("{}", Token::Anchor(anchor.clone())),
+ format!("{}", Token::Anchor(Box::new(anchor.clone()))),
"Tk:Anchor -> ",
);
anchor.text = String::from("FsJAt RTggA");
assert_eq!(
- format!("{}", Token::Anchor(anchor.clone())),
+ format!("{}", Token::Anchor(Box::new(anchor.clone()))),
"Tk:Anchor 'FsJAt RTggA' -> ",
);
anchor.text = String::from("wPVo1 0OmYm");
anchor.destination = Some(String::from("M1UEp 1gbfr"));
assert_eq!(
- format!("{}", Token::Anchor(anchor.clone())),
+ format!("{}", Token::Anchor(Box::new(anchor.clone()))),
r#"Tk:Anchor 'wPVo1 0OmYm' -> "M1UEp 1gbfr""#,
);
@@ -212,7 +248,7 @@ mod tests {
anchor.external = true;
assert_eq!(
- format!("{}", Token::Anchor(anchor.clone())),
+ format!("{}", Token::Anchor(Box::new(anchor.clone()))),
"Tk:Anchor 'wPVo1 0OmYm' -> \"M1UEp 1gbfr\" \
+Leading +Balanced +External",
);
@@ -230,7 +266,10 @@ mod tests {
let mut anchor = Anchor::default();
anchor.set_text("BSThI");
anchor.set_destination(Some(""));
- assert_eq!(anchor.render(), r#"BSThI "#);
+ assert_eq!(
+ anchor.render(),
+ r#"BSThI "#
+ );
}
#[test]
diff --git a/src/syntax/content/parser/token/bold.rs b/src/syntax/content/parser/token/bold.rs
index 15f2ec2..1003733 100644
--- a/src/syntax/content/parser/token/bold.rs
+++ b/src/syntax/content/parser/token/bold.rs
@@ -29,6 +29,10 @@ impl Parseable for Bold {
String::from("")
}
}
+
+ fn flatten(&self) -> String {
+ String::default()
+ }
}
impl std::fmt::Display for Bold {
diff --git a/src/syntax/content/parser/token/checkbox.rs b/src/syntax/content/parser/token/checkbox.rs
index e875e49..829996b 100644
--- a/src/syntax/content/parser/token/checkbox.rs
+++ b/src/syntax/content/parser/token/checkbox.rs
@@ -33,6 +33,10 @@ impl Parseable for CheckBox {
let toggle = if self.checked { " checked " } else { "" };
format!(r#" "#)
}
+
+ fn flatten(&self) -> String {
+ String::default()
+ }
}
impl std::fmt::Display for CheckBox {
diff --git a/src/syntax/content/parser/token/code.rs b/src/syntax/content/parser/token/code.rs
index 5ffd868..6dcd24b 100644
--- a/src/syntax/content/parser/token/code.rs
+++ b/src/syntax/content/parser/token/code.rs
@@ -29,6 +29,10 @@ impl Parseable for Code {
String::from("")
}
}
+
+ fn flatten(&self) -> String {
+ String::default()
+ }
}
impl std::fmt::Display for Code {
diff --git a/src/syntax/content/parser/token/header.rs b/src/syntax/content/parser/token/header.rs
index 8b35161..5c141ae 100644
--- a/src/syntax/content/parser/token/header.rs
+++ b/src/syntax/content/parser/token/header.rs
@@ -112,6 +112,10 @@ impl Parseable for Header {
panic!("Attempt to render a header tag while open state is unknown")
}
}
+
+ fn flatten(&self) -> String {
+ String::default()
+ }
}
impl std::fmt::Display for Header {
diff --git a/src/syntax/content/parser/token/item.rs b/src/syntax/content/parser/token/item.rs
index 3741520..585c981 100644
--- a/src/syntax/content/parser/token/item.rs
+++ b/src/syntax/content/parser/token/item.rs
@@ -18,6 +18,10 @@ impl Parseable for Item {
fn render(&self) -> String {
panic!("Items should only be rendered by a list's render method")
}
+
+ fn flatten(&self) -> String {
+ String::default()
+ }
}
impl Item {
diff --git a/src/syntax/content/parser/token/linebreak.rs b/src/syntax/content/parser/token/linebreak.rs
index 8c05284..2ec7123 100644
--- a/src/syntax/content/parser/token/linebreak.rs
+++ b/src/syntax/content/parser/token/linebreak.rs
@@ -17,6 +17,10 @@ impl Parseable for LineBreak {
fn render(&self) -> String {
"\n".to_owned()
}
+
+ fn flatten(&self) -> String {
+ String::from('\n')
+ }
}
impl std::fmt::Display for LineBreak {
diff --git a/src/syntax/content/parser/token/list.rs b/src/syntax/content/parser/token/list.rs
index a71f7bc..ece3fd2 100644
--- a/src/syntax/content/parser/token/list.rs
+++ b/src/syntax/content/parser/token/list.rs
@@ -51,6 +51,10 @@ impl Parseable for List {
format!("\n<{tag}>\n{output}{tag}>\n\n")
}
+
+ fn flatten(&self) -> String {
+ format!("[List: {} items]", self.items.len())
+ }
}
impl List {
diff --git a/src/syntax/content/parser/token/literal.rs b/src/syntax/content/parser/token/literal.rs
index 23d65b1..12ef542 100644
--- a/src/syntax/content/parser/token/literal.rs
+++ b/src/syntax/content/parser/token/literal.rs
@@ -19,6 +19,10 @@ impl Parseable for Literal {
fn render(&self) -> String {
self.text.clone()
}
+
+ fn flatten(&self) -> String {
+ self.text.clone()
+ }
}
impl std::fmt::Display for Literal {
diff --git a/src/syntax/content/parser/token/oblique.rs b/src/syntax/content/parser/token/oblique.rs
index b776bf6..49b7cad 100644
--- a/src/syntax/content/parser/token/oblique.rs
+++ b/src/syntax/content/parser/token/oblique.rs
@@ -29,6 +29,10 @@ impl Parseable for Oblique {
String::from("")
}
}
+
+ fn flatten(&self) -> String {
+ String::default()
+ }
}
impl std::fmt::Display for Oblique {
diff --git a/src/syntax/content/parser/token/paragraph.rs b/src/syntax/content/parser/token/paragraph.rs
index 500fb43..6b60c16 100644
--- a/src/syntax/content/parser/token/paragraph.rs
+++ b/src/syntax/content/parser/token/paragraph.rs
@@ -38,6 +38,10 @@ impl Parseable for Paragraph {
)
}
}
+
+ fn flatten(&self) -> String {
+ String::default()
+ }
}
impl std::fmt::Display for Paragraph {
diff --git a/src/syntax/content/parser/token/preformat.rs b/src/syntax/content/parser/token/preformat.rs
index e2ee75d..422f26c 100644
--- a/src/syntax/content/parser/token/preformat.rs
+++ b/src/syntax/content/parser/token/preformat.rs
@@ -46,6 +46,10 @@ impl Parseable for PreFormat {
)
}
}
+
+ fn flatten(&self) -> String {
+ String::default()
+ }
}
#[cfg(test)]
diff --git a/src/syntax/content/parser/token/strike.rs b/src/syntax/content/parser/token/strike.rs
index a13c868..ced9e7d 100644
--- a/src/syntax/content/parser/token/strike.rs
+++ b/src/syntax/content/parser/token/strike.rs
@@ -26,6 +26,10 @@ impl Parseable for Strike {
let tag = if self.open { "" } else { " " };
String::from(tag)
}
+
+ fn flatten(&self) -> String {
+ String::default()
+ }
}
impl std::fmt::Display for Strike {
diff --git a/src/syntax/content/parser/token/underline.rs b/src/syntax/content/parser/token/underline.rs
index a9a1d14..690f94d 100644
--- a/src/syntax/content/parser/token/underline.rs
+++ b/src/syntax/content/parser/token/underline.rs
@@ -29,6 +29,10 @@ impl Parseable for Underline {
String::from("")
}
}
+
+ fn flatten(&self) -> String {
+ String::default()
+ }
}
impl std::fmt::Display for Underline {
diff --git a/src/syntax/serial.rs b/src/syntax/serial.rs
index 9f2b298..0157333 100644
--- a/src/syntax/serial.rs
+++ b/src/syntax/serial.rs
@@ -1,8 +1,8 @@
use std::collections::HashMap;
use crate::{
- syntax::command::Arguments,
- types::{Edge, Graph, Meta, Node},
+ syntax::{command::Arguments, content::parser::flatten},
+ types::{Edge, Graph, Node},
};
pub fn populate_graph() -> Graph {
@@ -16,25 +16,25 @@ pub fn populate_graph() -> Graph {
modulate_graph(&graph)
}
-fn modulate_graph(graph: &Graph) -> Graph {
- let nodes = modulate_nodes(&graph.nodes);
+fn modulate_graph(in_graph: &Graph) -> Graph {
+ let nodes = modulate_nodes(in_graph);
- Graph {
+ let mut graph = Graph {
incoming: make_incoming(&nodes),
lowercase_keymap: map_lowercase_keys(&nodes),
nodes,
- meta: Meta {
- config: graph.meta.config.clone().parse_text(),
- ..graph.meta.clone()
- },
- ..graph.to_owned()
- }
+ ..in_graph.to_owned()
+ };
+
+ graph.parse();
+ graph
}
-fn modulate_nodes(old_nodes: &HashMap) -> HashMap {
+fn modulate_nodes(graph: &Graph) -> HashMap {
+ let old_nodes = graph.nodes.clone();
let mut nodes: HashMap = HashMap::default();
- for (key, node) in old_nodes {
+ for (key, node) in old_nodes.clone() {
let connections = node.connections.clone().unwrap_or_default();
let mut new_edges = connections.clone();
@@ -43,7 +43,7 @@ fn modulate_nodes(old_nodes: &HashMap) -> HashMap {
// Populate empty "from" IDs in edges with node's ID
if edge.from.is_empty() {
- new_edge.from.clone_from(key);
+ new_edge.from.clone_from(&key);
}
// Flag detached edges
@@ -62,7 +62,7 @@ fn modulate_nodes(old_nodes: &HashMap) -> HashMap {
from: key.clone(),
to: link.clone(),
anchor: String::default(),
- detached: !old_nodes.contains_key(link),
+ detached: !old_nodes.clone().contains_key(link),
});
}
@@ -73,9 +73,25 @@ fn modulate_nodes(old_nodes: &HashMap) -> HashMap {
node.title.clone()
};
+ let mut summary = if let Some(summary) = node.text.lines().next() {
+ if let Some(sentence) = node.text.split_once('.') {
+ format!("{}.", sentence.0)
+ } else {
+ String::from(summary)
+ }
+ } else {
+ node.text.clone()
+ };
+
+ if summary.len() > 300 {
+ summary.truncate(300);
+ summary.push('…');
+ }
+
let new_node = Node {
id: key.clone(),
title: new_title,
+ summary: flatten(&summary, graph),
connections: Some(new_edges),
..node.clone()
};
@@ -177,27 +193,6 @@ mod tests {
let message = graph.meta.messages.first().unwrap();
assert!(message.contains("expected value at line 1 column 1"));
}
-
- #[test]
- fn detached_node() {
- let mut node = Node::new(None);
- node.connections = Some(vec![Edge {
- anchor: String::from("SomeAnchor"),
- from: String::default(),
- to: String::default(),
- detached: false,
- }]);
-
- let mut map: HashMap = HashMap::default();
- map.insert(String::from("SomeNode"), node);
-
- let modulated_map = modulate_nodes(&map);
- let modulated_node = modulated_map.get("SomeNode").unwrap().clone();
- let modulated_connections = modulated_node.connections.unwrap();
- let modulated_connection = modulated_connections.first().unwrap();
- assert!(modulated_connection.anchor == "SomeAnchor");
- assert!(modulated_connection.detached);
- }
}
#[cfg(test)]
diff --git a/src/types.rs b/src/types.rs
index f99f268..31aa78d 100644
--- a/src/types.rs
+++ b/src/types.rs
@@ -23,6 +23,8 @@ pub struct Node {
#[serde(default)]
pub text: String,
#[serde(default)]
+ pub summary: String,
+ #[serde(default)]
pub title: String,
#[serde(default)]
pub links: Vec,
@@ -107,7 +109,7 @@ pub struct Config {
#[serde(default = "mktrue")]
pub tree: bool,
#[serde(default = "mkfalse")]
- pub tree_node_text: bool,
+ pub tree_node_summary: bool,
}
// See: https://github.com/serde-rs/serde/issues/368
@@ -121,6 +123,7 @@ fn mk8() -> u16 {
8
}
+#[derive(Clone)]
pub struct QueryResult {
pub node: Option,
pub redirect: bool,
@@ -144,7 +147,7 @@ impl Graph {
pub fn find_node(&self, query: &str) -> QueryResult {
let collapsed_query = query.trim().replace(" ", "");
- if let Some(exact_match) = self.nodes.get(query) {
+ let candidate = if let Some(exact_match) = self.nodes.get(query) {
QueryResult {
node: Some(exact_match.clone()),
redirect: false,
@@ -161,12 +164,30 @@ impl Graph {
node: None,
redirect: false,
}
+ };
+
+ if let Some(ref candidate_node) = candidate.node
+ && !candidate_node.redirect.is_empty()
+ {
+ QueryResult {
+ node: self.find_node(&candidate_node.redirect).node,
+ redirect: true,
+ }
+ } else {
+ candidate
}
}
pub fn get_root(&self) -> Option {
self.nodes.get(&self.root_node).cloned()
}
+
+ pub fn parse(&mut self) {
+ self.meta.config.footer_text =
+ content::parse(&self.meta.config.footer_text, self);
+ self.meta.config.about_text =
+ content::parse(&self.meta.config.about_text, self);
+ }
}
impl Node {
@@ -182,17 +203,7 @@ impl Node {
links: vec![],
redirect: String::default(),
hidden: false,
- }
- }
-}
-
-impl Config {
- #[must_use]
- pub fn parse_text(self) -> Config {
- Config {
- footer_text: content::parse(&self.footer_text, &self),
- about_text: content::parse(&self.about_text, &self),
- ..self
+ summary: String::default(),
}
}
}
@@ -222,7 +233,7 @@ impl Default for Config {
site_description: String::default(),
site_title: String::default(),
tree: true,
- tree_node_text: false,
+ tree_node_summary: false,
}
}
}
@@ -252,33 +263,35 @@ mod tests {
#[test]
fn empty_footer_text() {
- let default_graph = populate_graph();
+ let mut graph = populate_graph();
- let config = Config {
+ graph.meta.config = Config {
footer_text: String::default(),
- ..default_graph.meta.config
+ ..graph.meta.config
};
- let parsed_config = config.parse_text();
+ graph.parse();
- println!("{:?}", parsed_config.footer_text);
- assert!(parsed_config.footer_text.is_empty());
+ println!("{:?}", graph.meta.config.footer_text);
+ assert!(graph.meta.config.footer_text.is_empty());
}
#[test]
fn config_footer_text() {
let payload = "0kqBrdS8NPrU4xVxh2xW0hUzAw926JCQ";
- let default_graph = populate_graph();
+ let mut graph = populate_graph();
- let config = Config {
+ graph.meta.config = Config {
footer_text: format!("`{payload}`"),
- ..default_graph.meta.config
+ ..graph.meta.config
};
- let parsed_config = config.parse_text();
+ graph.parse();
assert!(
- parsed_config
+ graph
+ .meta
+ .config
.footer_text
.matches(format!("{payload}").as_str())
.count()
@@ -289,17 +302,19 @@ mod tests {
#[test]
fn config_about_text() {
let payload = "ZqPFl84JlzSS0QUo61RwTUPONIE78Lmw";
- let default_graph = populate_graph();
+ let mut graph = populate_graph();
- let config = Config {
+ graph.meta.config = Config {
about_text: format!("`{payload}`"),
- ..default_graph.meta.config
+ ..graph.meta.config
};
- let parsed_config = config.parse_text();
+ graph.parse();
assert!(
- parsed_config
+ graph
+ .meta
+ .config
.about_text
.matches(format!("{payload}").as_str())
.count()
diff --git a/static/graph.toml b/static/graph.toml
index e433e38..7830df3 100644
--- a/static/graph.toml
+++ b/static/graph.toml
@@ -520,7 +520,7 @@ en is only possible thanks to a number of projects and people:
[nodes.Roadmap]
text = """
- [x] Redirects
-- [ ] Strip/render some syntax in Tree text preview
+- [x] Strip/render some syntax in Tree text preview
- [x] Drop-down navigation
- [ ] Meta-awareness
- [ ] Detached edges
@@ -540,7 +540,7 @@ text = """
- [ ] By most linked to
- [ ] By most linked
- [x] Anchors and connections
- - [ ] Render detached anchors differently
+ - [x] Render detached anchors differently
- [ ] Suffix-aware anchors
- [x] Plural anchors (`|node|s` -> `node`)
- [x] Ignore trailing punctuation
@@ -560,6 +560,7 @@ text = """
- [x] External anchors
- [x] Richer text formatting
- [x] Nested formatting
+ - [ ] Blockquotes
- [x] Headers
- [x] Preformatted blocks
- [x] _Oblique_,
diff --git a/static/style.css b/static/style.css
index 0842b68..c62f865 100644
--- a/static/style.css
+++ b/static/style.css
@@ -37,7 +37,12 @@ a {
}
a:visited {
- text-decoration-color: #aaa;
+ text-decoration-color: #999;
+}
+
+a.detached {
+ color: #595959;
+ text-decoration-color: #555555;
}
div.header-row {
@@ -75,24 +80,6 @@ h1.node-title {
margin: 10px 0;
}
-ul.tree-node-text {
- display: inline;
- padding-left: 0;
-}
-
-li.tree-node-text {
- display: inline;
-}
-
-details.tree-node-text {
- display: inline;
- cursor: pointer;
-}
-
-summary.tree-node-text {
- display: inline;
-}
-
footer div {
margin: 20px 0;
text-align: center;
@@ -180,6 +167,11 @@ em#index-node-count {
text-decoration-color: #159b9b;
}
+ a.detached {
+ color: #acacac;
+ text-decoration-color: #777;
+ }
+
span.id-label {
background-color: #444;
border-color: #666;
diff --git a/templates/tree.html b/templates/tree.html
index ec6d4df..9e346ce 100644
--- a/templates/tree.html
+++ b/templates/tree.html
@@ -15,28 +15,20 @@
{{root_node.title}}
- {% if root_node.connections or config.tree_node_text %}
+ {% if root_node.connections or config.tree_node_summary %}
- {% if config.tree_node_text %}
- Text:
-
-
-
-
- {{root_node.text | truncate(length=120)}}
-
- {{root_node.text}}
-
+ {% if config.tree_node_summary %}
+
+ {{ root_node.summary }}
-
{% endif %}
{% if root_node.connections %}
- {% if config.tree_node_text %}Connections
+ {% if config.tree_node_summary %}Connections
{% endif %}
{% for connection in root_node.connections %}
{{connection.to}}
{% endfor %}
- {% if config.tree_node_text %}
+ {% if config.tree_node_summary %}
{% endif %}
{% endif %}
@@ -51,30 +43,22 @@
{% for node in nodes | filter(attribute="hidden", value=false)%}
{{node.title}}
- {% if node.connections or config.tree_node_text %}
+ {% if node.connections or config.tree_node_summary %}
- {% if config.tree_node_text %}
- Text:
-
-
-
-
- {{node.text | truncate(length=30)}}
-
- {{node.text}}
-
+ {% if config.tree_node_summary %}
+
+ {{node.summary}}
-
{% endif %}
{% if node.connections %}
- {% if config.tree_node_text %}Connections
+ {% if config.tree_node_summary %}Connections
{% endif %}
{% for connection in node.connections %}
{% if not connection.detached %}
{{connection.to}}
{% endif %}
{% endfor %}
- {% if config.tree_node_text %}
+ {% if config.tree_node_summary %}
{% endif %}
{% endif %}