Reference styling
This commit is contained in:
parent
ea0a0cf5b1
commit
ffd1903a65
4 changed files with 221 additions and 77 deletions
|
@ -77,6 +77,51 @@ impl<'a> Compiler<'a> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Sanitizes a format string for a [`Target`]
|
||||||
|
///
|
||||||
|
/// # Notes
|
||||||
|
///
|
||||||
|
/// This function may process invalid format string, which will be caught later
|
||||||
|
/// by runtime_format.
|
||||||
|
pub fn sanitize_format<S: AsRef<str>>(target: Target, str: S) -> String {
|
||||||
|
match target {
|
||||||
|
Target::HTML => {
|
||||||
|
let mut out = String::new();
|
||||||
|
|
||||||
|
let mut braces = 0;
|
||||||
|
for c in str.as_ref().chars() {
|
||||||
|
if c == '{' {
|
||||||
|
out.push(c);
|
||||||
|
braces += 1;
|
||||||
|
continue;
|
||||||
|
} else if c == '}' {
|
||||||
|
out.push(c);
|
||||||
|
if braces != 0 {
|
||||||
|
braces -= 1;
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
// Inside format args
|
||||||
|
if braces % 2 == 1 {
|
||||||
|
out.push(c);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
match c {
|
||||||
|
'&' => out += "&",
|
||||||
|
'<' => out += "<",
|
||||||
|
'>' => out += ">",
|
||||||
|
'"' => out += """,
|
||||||
|
_ => out.push(c),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
out
|
||||||
|
}
|
||||||
|
_ => todo!("Sanitize not implemented"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Gets a reference name
|
/// Gets a reference name
|
||||||
pub fn refname<S: AsRef<str>>(target: Target, str: S) -> String {
|
pub fn refname<S: AsRef<str>>(target: Target, str: S) -> String {
|
||||||
Self::sanitize(target, str).replace(' ', "_")
|
Self::sanitize(target, str).replace(' ', "_")
|
||||||
|
@ -131,9 +176,7 @@ impl<'a> Compiler<'a> {
|
||||||
document: &dyn Document,
|
document: &dyn Document,
|
||||||
var_name: &'static str,
|
var_name: &'static str,
|
||||||
) -> Option<Rc<dyn Variable>> {
|
) -> Option<Rc<dyn Variable>> {
|
||||||
document
|
document.get_variable(var_name).or_else(|| {
|
||||||
.get_variable(var_name)
|
|
||||||
.or_else(|| {
|
|
||||||
println!(
|
println!(
|
||||||
"Missing variable `{var_name}` in {}",
|
"Missing variable `{var_name}` in {}",
|
||||||
document.source().name()
|
document.source().name()
|
||||||
|
@ -308,7 +351,7 @@ impl CompiledDocument {
|
||||||
.ok()
|
.ok()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Inserts [`CompiledDocument`] into cache
|
/// Interts [`CompiledDocument`] into cache
|
||||||
pub fn insert_cache(&self, con: &Connection) -> Result<usize, rusqlite::Error> {
|
pub fn insert_cache(&self, con: &Connection) -> Result<usize, rusqlite::Error> {
|
||||||
con.execute(
|
con.execute(
|
||||||
Self::sql_insert_query(),
|
Self::sql_insert_query(),
|
||||||
|
@ -324,3 +367,19 @@ impl CompiledDocument {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn sanitize_test() {
|
||||||
|
assert_eq!(Compiler::sanitize(Target::HTML, "<a>"), "<a>");
|
||||||
|
assert_eq!(Compiler::sanitize(Target::HTML, "<"), "&lt;");
|
||||||
|
assert_eq!(Compiler::sanitize(Target::HTML, "\""), """);
|
||||||
|
|
||||||
|
assert_eq!(Compiler::sanitize_format(Target::HTML, "{<>&\"}"), "{<>&\"}");
|
||||||
|
assert_eq!(Compiler::sanitize_format(Target::HTML, "{{<>}}"), "{{<>}}");
|
||||||
|
assert_eq!(Compiler::sanitize_format(Target::HTML, "{{<"), "{{<");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -231,7 +231,7 @@ pub fn create_navigation(
|
||||||
|
|
||||||
// Sort entries
|
// Sort entries
|
||||||
fn sort_entries(nav: &mut NavEntries) {
|
fn sort_entries(nav: &mut NavEntries) {
|
||||||
let mut entrymap = nav
|
let entrymap = nav
|
||||||
.entries
|
.entries
|
||||||
.iter()
|
.iter()
|
||||||
.map(|ent| (ent.title.clone(), ent.previous.clone()))
|
.map(|ent| (ent.title.clone(), ent.previous.clone()))
|
||||||
|
@ -250,8 +250,8 @@ pub fn create_navigation(
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
|
use rand::prelude::SliceRandom;
|
||||||
use rand::rngs::OsRng;
|
use rand::rngs::OsRng;
|
||||||
use rand::RngCore;
|
|
||||||
|
|
||||||
use crate::compiler::process::process_from_memory;
|
use crate::compiler::process::process_from_memory;
|
||||||
|
|
||||||
|
@ -288,12 +288,10 @@ mod tests {
|
||||||
];
|
];
|
||||||
let mut shuffled = entries.clone();
|
let mut shuffled = entries.clone();
|
||||||
for _ in 0..10 {
|
for _ in 0..10 {
|
||||||
for i in 0..5 {
|
let mut rng = OsRng {};
|
||||||
let pos = OsRng.next_u64() % entries.len() as u64;
|
shuffled.shuffle(&mut rng);
|
||||||
shuffled.swap(i, pos as usize);
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut entrymap = shuffled
|
let entrymap = shuffled
|
||||||
.iter()
|
.iter()
|
||||||
.map(|ent| (ent.title.clone(), ent.previous.clone()))
|
.map(|ent| (ent.title.clone(), ent.previous.clone()))
|
||||||
.collect::<HashMap<String, Option<String>>>();
|
.collect::<HashMap<String, Option<String>>>();
|
||||||
|
|
|
@ -87,33 +87,29 @@ impl Element for Blockquote {
|
||||||
match compiler.target() {
|
match compiler.target() {
|
||||||
HTML => {
|
HTML => {
|
||||||
let mut result = r#"<div class="blockquote-content">"#.to_string();
|
let mut result = r#"<div class="blockquote-content">"#.to_string();
|
||||||
let format_author = || -> Result<String, FormatError> {
|
let format_author = || -> Result<String, String> {
|
||||||
let mut result = String::new();
|
let mut result = String::new();
|
||||||
|
|
||||||
if self.cite.is_some() || self.author.is_some() {
|
if self.cite.is_some() || self.author.is_some() {
|
||||||
result += r#"<p class="blockquote-author">"#;
|
result += r#"<p class="blockquote-author">"#;
|
||||||
let fmt_pair = FmtPair(compiler.target(), self);
|
let fmt_pair = FmtPair(compiler.target(), self);
|
||||||
match (self.author.is_some(), self.cite.is_some()) {
|
let format_string = match (self.author.is_some(), self.cite.is_some()) {
|
||||||
(true, true) => {
|
(true, true) => {
|
||||||
let args =
|
Compiler::sanitize_format(fmt_pair.0, self.style.format[0].as_str())
|
||||||
FormatArgs::new(self.style.format[0].as_str(), &fmt_pair);
|
|
||||||
args.status()?;
|
|
||||||
result += args.to_string().as_str();
|
|
||||||
}
|
}
|
||||||
(true, false) => {
|
(true, false) => {
|
||||||
let args =
|
Compiler::sanitize_format(fmt_pair.0, self.style.format[1].as_str())
|
||||||
FormatArgs::new(self.style.format[1].as_str(), &fmt_pair);
|
|
||||||
args.status()?;
|
|
||||||
result += args.to_string().as_str();
|
|
||||||
}
|
}
|
||||||
(false, false) => {
|
(false, false) => {
|
||||||
let args =
|
Compiler::sanitize_format(fmt_pair.0, self.style.format[2].as_str())
|
||||||
FormatArgs::new(self.style.format[2].as_str(), &fmt_pair);
|
|
||||||
args.status()?;
|
|
||||||
result += args.to_string().as_str();
|
|
||||||
}
|
}
|
||||||
_ => panic!(""),
|
_ => panic!(""),
|
||||||
}
|
};
|
||||||
|
let args = FormatArgs::new(format_string.as_str(), &fmt_pair);
|
||||||
|
args.status().map_err(|err| {
|
||||||
|
format!("Failed to format Blockquote style `{format_string}`: {err}")
|
||||||
|
})?;
|
||||||
|
result += args.to_string().as_str();
|
||||||
result += "</p>";
|
result += "</p>";
|
||||||
}
|
}
|
||||||
Ok(result)
|
Ok(result)
|
||||||
|
@ -126,25 +122,21 @@ impl Element for Blockquote {
|
||||||
result += "<blockquote>";
|
result += "<blockquote>";
|
||||||
}
|
}
|
||||||
if self.style.author_pos == Before {
|
if self.style.author_pos == Before {
|
||||||
result += format_author().map_err(|err| err.to_string())?.as_str();
|
result += format_author()?.as_str();
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut in_paragraph = false;
|
let mut in_paragraph = false;
|
||||||
for elem in &self.content {
|
for elem in &self.content {
|
||||||
if elem.downcast_ref::<DocumentEnd>().is_some() {}
|
if elem.downcast_ref::<DocumentEnd>().is_some() {
|
||||||
else if elem.downcast_ref::<Blockquote>().is_some()
|
} else if elem.downcast_ref::<Blockquote>().is_some() {
|
||||||
{
|
if in_paragraph {
|
||||||
if in_paragraph
|
|
||||||
{
|
|
||||||
result += "</p>";
|
result += "</p>";
|
||||||
in_paragraph = false;
|
in_paragraph = false;
|
||||||
}
|
}
|
||||||
result += elem
|
result += elem
|
||||||
.compile(compiler, document, cursor + result.len())?
|
.compile(compiler, document, cursor + result.len())?
|
||||||
.as_str();
|
.as_str();
|
||||||
}
|
} else {
|
||||||
else
|
|
||||||
{
|
|
||||||
if !in_paragraph {
|
if !in_paragraph {
|
||||||
result += "<p>";
|
result += "<p>";
|
||||||
in_paragraph = true;
|
in_paragraph = true;
|
||||||
|
@ -154,8 +146,7 @@ impl Element for Blockquote {
|
||||||
.as_str();
|
.as_str();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if in_paragraph
|
if in_paragraph {
|
||||||
{
|
|
||||||
result += "</p>";
|
result += "</p>";
|
||||||
}
|
}
|
||||||
result += "</blockquote>";
|
result += "</blockquote>";
|
||||||
|
@ -299,7 +290,6 @@ impl Rule for BlockquoteRule {
|
||||||
// Content
|
// Content
|
||||||
let entry_start = captures.get(0).unwrap().start();
|
let entry_start = captures.get(0).unwrap().start();
|
||||||
let mut entry_content = captures.get(2).unwrap().as_str().to_string();
|
let mut entry_content = captures.get(2).unwrap().as_str().to_string();
|
||||||
println!("f={entry_content}");
|
|
||||||
while let Some(captures) = self.continue_re.captures_at(content, end_cursor.pos) {
|
while let Some(captures) = self.continue_re.captures_at(content, end_cursor.pos) {
|
||||||
if captures.get(0).unwrap().start() != end_cursor.pos {
|
if captures.get(0).unwrap().start() != end_cursor.pos {
|
||||||
break;
|
break;
|
||||||
|
@ -308,17 +298,12 @@ impl Rule for BlockquoteRule {
|
||||||
end_cursor = end_cursor.at(captures.get(0).unwrap().end());
|
end_cursor = end_cursor.at(captures.get(0).unwrap().end());
|
||||||
|
|
||||||
let trimmed = captures.get(1).unwrap().as_str().trim_start().trim_end();
|
let trimmed = captures.get(1).unwrap().as_str().trim_start().trim_end();
|
||||||
println!("tr={trimmed}");
|
|
||||||
//if !trimmed.is_empty()
|
|
||||||
{
|
|
||||||
entry_content += "\n";
|
entry_content += "\n";
|
||||||
entry_content += trimmed;
|
entry_content += trimmed;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// Parse entry content
|
// Parse entry content
|
||||||
let token = Token::new(entry_start..end_cursor.pos, end_cursor.source.clone());
|
let token = Token::new(entry_start..end_cursor.pos, end_cursor.source.clone());
|
||||||
println!("{entry_content}.");
|
|
||||||
let entry_src = Rc::new(VirtualSource::new(
|
let entry_src = Rc::new(VirtualSource::new(
|
||||||
token.clone(),
|
token.clone(),
|
||||||
"Blockquote Entry".to_string(),
|
"Blockquote Entry".to_string(),
|
||||||
|
@ -326,33 +311,31 @@ impl Rule for BlockquoteRule {
|
||||||
));
|
));
|
||||||
// Parse content
|
// Parse content
|
||||||
let parsed_doc = state.with_state(|new_state| {
|
let parsed_doc = state.with_state(|new_state| {
|
||||||
new_state.parser.parse(new_state, entry_src, Some(document)).0
|
new_state
|
||||||
|
.parser
|
||||||
|
.parse(new_state, entry_src, Some(document))
|
||||||
|
.0
|
||||||
});
|
});
|
||||||
|
|
||||||
// Extract paragraph and nested blockquotes
|
// Extract paragraph and nested blockquotes
|
||||||
let mut parsed_content: Vec<Box<dyn Element>> = vec![];
|
let mut parsed_content: Vec<Box<dyn Element>> = vec![];
|
||||||
for mut elem in parsed_doc.content().borrow_mut().drain(..)
|
for mut elem in parsed_doc.content().borrow_mut().drain(..) {
|
||||||
{
|
if let Some(paragraph) = elem.downcast_mut::<Paragraph>() {
|
||||||
if let Some(paragraph) = elem.downcast_mut::<Paragraph>()
|
if let Some(last) = parsed_content.last() {
|
||||||
{
|
if last.kind() == ElemKind::Inline {
|
||||||
if let Some(last) = parsed_content.last()
|
|
||||||
{
|
|
||||||
if last.kind() == ElemKind::Inline
|
|
||||||
{
|
|
||||||
parsed_content.push(Box::new(Text {
|
parsed_content.push(Box::new(Text {
|
||||||
location: Token::new(last.location().end()..last.location().end(), last.location().source()),
|
location: Token::new(
|
||||||
content: " ".to_string()
|
last.location().end()..last.location().end(),
|
||||||
|
last.location().source(),
|
||||||
|
),
|
||||||
|
content: " ".to_string(),
|
||||||
}) as Box<dyn Element>);
|
}) as Box<dyn Element>);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
parsed_content.extend(std::mem::take(&mut paragraph.content));
|
parsed_content.extend(std::mem::take(&mut paragraph.content));
|
||||||
}
|
} else if elem.downcast_ref::<Blockquote>().is_some() {
|
||||||
else if elem.downcast_ref::<Blockquote>().is_some()
|
|
||||||
{
|
|
||||||
parsed_content.push(elem);
|
parsed_content.push(elem);
|
||||||
}
|
} else {
|
||||||
else
|
|
||||||
{
|
|
||||||
reports.push(
|
reports.push(
|
||||||
Report::build(ReportKind::Error, token.source(), token.range.start)
|
Report::build(ReportKind::Error, token.source(), token.range.start)
|
||||||
.with_message("Unable to Parse Blockquote Entry")
|
.with_message("Unable to Parse Blockquote Entry")
|
||||||
|
|
|
@ -5,9 +5,13 @@ use std::rc::Rc;
|
||||||
use ariadne::Label;
|
use ariadne::Label;
|
||||||
use ariadne::Report;
|
use ariadne::Report;
|
||||||
use ariadne::ReportKind;
|
use ariadne::ReportKind;
|
||||||
|
use reference_style::ExternalReferenceStyle;
|
||||||
use regex::Captures;
|
use regex::Captures;
|
||||||
use regex::Match;
|
use regex::Match;
|
||||||
use regex::Regex;
|
use regex::Regex;
|
||||||
|
use runtime_format::FormatArgs;
|
||||||
|
use runtime_format::FormatKey;
|
||||||
|
use runtime_format::FormatKeyError;
|
||||||
|
|
||||||
use crate::compiler::compiler::Compiler;
|
use crate::compiler::compiler::Compiler;
|
||||||
use crate::compiler::compiler::Target;
|
use crate::compiler::compiler::Target;
|
||||||
|
@ -21,6 +25,7 @@ use crate::parser::parser::ReportColors;
|
||||||
use crate::parser::rule::RegexRule;
|
use crate::parser::rule::RegexRule;
|
||||||
use crate::parser::source::Source;
|
use crate::parser::source::Source;
|
||||||
use crate::parser::source::Token;
|
use crate::parser::source::Token;
|
||||||
|
use crate::parser::style::StyleHolder;
|
||||||
use crate::parser::util;
|
use crate::parser::util;
|
||||||
use crate::parser::util::Property;
|
use crate::parser::util::Property;
|
||||||
use crate::parser::util::PropertyMap;
|
use crate::parser::util::PropertyMap;
|
||||||
|
@ -52,7 +57,12 @@ impl Element for InternalReference {
|
||||||
) -> Result<String, String> {
|
) -> Result<String, String> {
|
||||||
match compiler.target() {
|
match compiler.target() {
|
||||||
Target::HTML => {
|
Target::HTML => {
|
||||||
let elemref = document.get_reference(self.refname.as_str()).ok_or(format!("Unable to find reference `{}` in current document", self.refname))?;
|
let elemref = document
|
||||||
|
.get_reference(self.refname.as_str())
|
||||||
|
.ok_or(format!(
|
||||||
|
"Unable to find reference `{}` in current document",
|
||||||
|
self.refname
|
||||||
|
))?;
|
||||||
let elem = document.get_from_reference(&elemref).unwrap();
|
let elem = document.get_from_reference(&elemref).unwrap();
|
||||||
|
|
||||||
elem.compile_reference(
|
elem.compile_reference(
|
||||||
|
@ -72,6 +82,29 @@ pub struct ExternalReference {
|
||||||
pub(self) location: Token,
|
pub(self) location: Token,
|
||||||
pub(self) reference: CrossReference,
|
pub(self) reference: CrossReference,
|
||||||
pub(self) caption: Option<String>,
|
pub(self) caption: Option<String>,
|
||||||
|
pub(self) style: Rc<reference_style::ExternalReferenceStyle>,
|
||||||
|
}
|
||||||
|
|
||||||
|
struct FmtPair<'a>(Target, &'a ExternalReference);
|
||||||
|
|
||||||
|
impl FormatKey for FmtPair<'_> {
|
||||||
|
fn fmt(&self, key: &str, f: &mut std::fmt::Formatter<'_>) -> Result<(), FormatKeyError> {
|
||||||
|
match &self.1.reference {
|
||||||
|
CrossReference::Unspecific(refname) => match key {
|
||||||
|
"refname" => write!(f, "{}", Compiler::sanitize(self.0, refname))
|
||||||
|
.map_err(FormatKeyError::Fmt),
|
||||||
|
_ => Err(FormatKeyError::UnknownKey),
|
||||||
|
},
|
||||||
|
CrossReference::Specific(refdoc, refname) => match key {
|
||||||
|
"refdoc" => {
|
||||||
|
write!(f, "{}", Compiler::sanitize(self.0, refdoc)).map_err(FormatKeyError::Fmt)
|
||||||
|
}
|
||||||
|
"refname" => write!(f, "{}", Compiler::sanitize(self.0, refname))
|
||||||
|
.map_err(FormatKeyError::Fmt),
|
||||||
|
_ => Err(FormatKeyError::UnknownKey),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Element for ExternalReference {
|
impl Element for ExternalReference {
|
||||||
|
@ -91,14 +124,34 @@ impl Element for ExternalReference {
|
||||||
Target::HTML => {
|
Target::HTML => {
|
||||||
let mut result = "<a href=\"".to_string();
|
let mut result = "<a href=\"".to_string();
|
||||||
|
|
||||||
compiler.insert_crossreference(cursor + result.len(), self.reference.clone());
|
// Link position
|
||||||
|
let crossreference_pos = cursor + result.len();
|
||||||
|
|
||||||
if let Some(caption) = &self.caption {
|
if let Some(caption) = &self.caption {
|
||||||
result +=
|
result +=
|
||||||
format!("\">{}</a>", Compiler::sanitize(Target::HTML, caption)).as_str();
|
format!("\">{}</a>", Compiler::sanitize(Target::HTML, caption)).as_str();
|
||||||
} else {
|
} else {
|
||||||
result += format!("\">{}</a>", self.reference).as_str();
|
// Use style
|
||||||
|
let fmt_pair = FmtPair(compiler.target(), self);
|
||||||
|
let format_string = match &self.reference {
|
||||||
|
CrossReference::Unspecific(_) => Compiler::sanitize_format(
|
||||||
|
fmt_pair.0,
|
||||||
|
self.style.format_unspecific.as_str(),
|
||||||
|
),
|
||||||
|
CrossReference::Specific(_, _) => Compiler::sanitize_format(
|
||||||
|
fmt_pair.0,
|
||||||
|
self.style.format_specific.as_str(),
|
||||||
|
),
|
||||||
|
};
|
||||||
|
let args = FormatArgs::new(format_string.as_str(), &fmt_pair);
|
||||||
|
args.status().map_err(|err| {
|
||||||
|
format!("Failed to format ExternalReference style `{format_string}`: {err}")
|
||||||
|
})?;
|
||||||
|
|
||||||
|
result += format!("\">{}</a>", args.to_string()).as_str();
|
||||||
}
|
}
|
||||||
|
// Add crossreference
|
||||||
|
compiler.insert_crossreference(crossreference_pos, self.reference.clone());
|
||||||
Ok(result)
|
Ok(result)
|
||||||
}
|
}
|
||||||
_ => todo!(""),
|
_ => todo!(""),
|
||||||
|
@ -223,7 +276,7 @@ impl RegexRule for ReferenceRule {
|
||||||
);
|
);
|
||||||
return reports;
|
return reports;
|
||||||
}
|
}
|
||||||
Ok(refname) => (None, refname.to_string())
|
Ok(refname) => (None, refname.to_string()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
@ -244,9 +297,20 @@ impl RegexRule for ReferenceRule {
|
||||||
.get("caption", |_, value| -> Result<String, ()> {
|
.get("caption", |_, value| -> Result<String, ()> {
|
||||||
Ok(value.clone())
|
Ok(value.clone())
|
||||||
})
|
})
|
||||||
.ok().map(|(_, s)| s);
|
.ok()
|
||||||
|
.map(|(_, s)| s);
|
||||||
|
|
||||||
if let Some(refdoc) = refdoc {
|
if let Some(refdoc) = refdoc {
|
||||||
|
// Get style
|
||||||
|
let style = state
|
||||||
|
.shared
|
||||||
|
.styles
|
||||||
|
.borrow()
|
||||||
|
.current(reference_style::STYLE_KEY)
|
||||||
|
.downcast_rc::<reference_style::ExternalReferenceStyle>()
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
// §{#refname}
|
||||||
if refdoc.is_empty() {
|
if refdoc.is_empty() {
|
||||||
state.push(
|
state.push(
|
||||||
document,
|
document,
|
||||||
|
@ -254,8 +318,10 @@ impl RegexRule for ReferenceRule {
|
||||||
location: token,
|
location: token,
|
||||||
reference: CrossReference::Unspecific(refname),
|
reference: CrossReference::Unspecific(refname),
|
||||||
caption,
|
caption,
|
||||||
|
style,
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
|
// §{docname#refname}
|
||||||
} else {
|
} else {
|
||||||
state.push(
|
state.push(
|
||||||
document,
|
document,
|
||||||
|
@ -263,6 +329,7 @@ impl RegexRule for ReferenceRule {
|
||||||
location: token,
|
location: token,
|
||||||
reference: CrossReference::Specific(refdoc, refname),
|
reference: CrossReference::Specific(refdoc, refname),
|
||||||
caption,
|
caption,
|
||||||
|
style,
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -279,6 +346,36 @@ impl RegexRule for ReferenceRule {
|
||||||
|
|
||||||
reports
|
reports
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn register_styles(&self, holder: &mut StyleHolder) {
|
||||||
|
holder.set_current(Rc::new(ExternalReferenceStyle::default()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
mod reference_style {
|
||||||
|
use serde::Deserialize;
|
||||||
|
use serde::Serialize;
|
||||||
|
|
||||||
|
use crate::impl_elementstyle;
|
||||||
|
|
||||||
|
pub static STYLE_KEY: &str = "style.external_reference";
|
||||||
|
|
||||||
|
#[derive(Debug, Serialize, Deserialize)]
|
||||||
|
pub struct ExternalReferenceStyle {
|
||||||
|
pub format_unspecific: String,
|
||||||
|
pub format_specific: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for ExternalReferenceStyle {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self {
|
||||||
|
format_unspecific: "(#{refname})".into(),
|
||||||
|
format_specific: "({refdoc}#{refname})".into(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl_elementstyle!(ExternalReferenceStyle, STYLE_KEY);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
|
@ -372,15 +469,22 @@ mod tests {
|
||||||
r#"
|
r#"
|
||||||
@html.page_title = 2
|
@html.page_title = 2
|
||||||
|
|
||||||
|
@@style.external_reference = {
|
||||||
|
"format_unspecific": "[UNSPECIFIC {refname}]",
|
||||||
|
"format_specific": "[SPECIFIC {refdoc}:{refname}]"
|
||||||
|
}
|
||||||
|
|
||||||
§{#ref}[caption=from 0]
|
§{#ref}[caption=from 0]
|
||||||
|
§{#ref}
|
||||||
§{#ref2}[caption=from 1]
|
§{#ref2}[caption=from 1]
|
||||||
|
§{b#ref2}
|
||||||
"#
|
"#
|
||||||
.into(),
|
.into(),
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
assert!(result[1].0.borrow().body.starts_with("<div class=\"content\"><p><a href=\"a.html#Referenceable_section\">#ref</a><a href=\"a.html#Referenceable_section\">a#ref</a></p>"));
|
assert!(result[1].0.borrow().body.starts_with("<div class=\"content\"><p><a href=\"a.html#Referenceable_section\">(#ref)</a><a href=\"a.html#Referenceable_section\">(a#ref)</a></p>"));
|
||||||
assert!(result[2].0.borrow().body.starts_with("<div class=\"content\"><p><a href=\"a.html#Referenceable_section\">from 0</a><a href=\"b.html#Another_Referenceable_section\">from 1</a></p>"));
|
assert!(result[2].0.borrow().body.starts_with("<div class=\"content\"><p><a href=\"a.html#Referenceable_section\">from 0</a><a href=\"a.html#Referenceable_section\">[UNSPECIFIC ref]</a><a href=\"b.html#Another_Referenceable_section\">from 1</a><a href=\"b.html#Another_Referenceable_section\">[SPECIFIC b:ref2]</a></p>"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue