Smart links
This commit is contained in:
@@ -1,4 +1,5 @@
|
||||
use lazy_static::lazy_static;
|
||||
use markdown_it::generics::inline::full_link;
|
||||
use markdown_it::parser::block::{BlockRule, BlockState};
|
||||
use markdown_it::parser::core::Root;
|
||||
use markdown_it::parser::inline::{InlineRule, InlineState};
|
||||
@@ -9,12 +10,47 @@ use std::str::FromStr;
|
||||
|
||||
use crate::components::fa::FAIcon;
|
||||
use crate::components::mangle::{MangledBetaEmail, MangledGoogleEmail};
|
||||
use crate::components::misc::{Backlinks, FarLink};
|
||||
use crate::components::misc::Backlinks;
|
||||
|
||||
lazy_static! {
|
||||
static ref MdParser: MarkdownIt = {
|
||||
let mut md = markdown_it::MarkdownIt::new();
|
||||
markdown_it::plugins::cmark::add(&mut md);
|
||||
|
||||
{
|
||||
|
||||
use markdown_it::plugins::cmark::*;
|
||||
|
||||
inline::newline::add(&mut md);
|
||||
inline::escape::add(&mut md);
|
||||
inline::backticks::add(&mut md);
|
||||
inline::emphasis::add(&mut md);
|
||||
|
||||
// Replaced with smart links
|
||||
//inline::link::add(&mut md);
|
||||
full_link::add::<false>(&mut md, |href, title| {
|
||||
Node::new(SmartLink {
|
||||
url: href.unwrap_or_default(),
|
||||
title,
|
||||
})
|
||||
});
|
||||
|
||||
|
||||
inline::image::add(&mut md);
|
||||
inline::autolink::add(&mut md);
|
||||
inline::entity::add(&mut md);
|
||||
|
||||
block::code::add(&mut md);
|
||||
block::fence::add(&mut md);
|
||||
block::blockquote::add(&mut md);
|
||||
block::hr::add(&mut md);
|
||||
block::list::add(&mut md);
|
||||
block::reference::add(&mut md);
|
||||
block::heading::add(&mut md);
|
||||
block::lheading::add(&mut md);
|
||||
block::paragraph::add(&mut md);
|
||||
|
||||
}
|
||||
|
||||
markdown_it::plugins::html::add(&mut md);
|
||||
|
||||
md.block.add_rule::<YamlFrontMatter>().before_all();
|
||||
@@ -100,6 +136,39 @@ pub fn page_from_markdown(md: impl Into<String>, default_image: Option<String>)
|
||||
// MARK: extensions
|
||||
//
|
||||
|
||||
//
|
||||
// MARK: smart link
|
||||
//
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct SmartLink {
|
||||
pub url: String,
|
||||
pub title: Option<String>,
|
||||
}
|
||||
|
||||
impl NodeValue for SmartLink {
|
||||
fn render(&self, node: &Node, fmt: &mut dyn Renderer) {
|
||||
let mut attrs = node.attrs.clone();
|
||||
attrs.push(("href", self.url.clone()));
|
||||
|
||||
if let Some(title) = &self.title {
|
||||
attrs.push(("title", title.clone()));
|
||||
}
|
||||
|
||||
let external = !(self.url.starts_with(".") || self.url.starts_with("/"));
|
||||
|
||||
// Open external links in a new tab
|
||||
if external {
|
||||
attrs.push(("target", "_blank".into()));
|
||||
attrs.push(("rel", "noopener noreferrer".into()));
|
||||
}
|
||||
|
||||
fmt.open("a", &attrs);
|
||||
fmt.contents(&node.children);
|
||||
fmt.close("a");
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// MARK: emote
|
||||
//
|
||||
@@ -159,10 +228,6 @@ impl NodeValue for InlineMdx {
|
||||
return;
|
||||
}
|
||||
|
||||
if mdx_external(&self.0, node, fmt) {
|
||||
return;
|
||||
}
|
||||
|
||||
fmt.open("code", &[]);
|
||||
fmt.text(&self.0);
|
||||
fmt.close("code");
|
||||
@@ -340,105 +405,6 @@ fn mdx_include(mdx: &str, _node: &Node, fmt: &mut dyn Renderer) -> bool {
|
||||
return true;
|
||||
}
|
||||
|
||||
fn mdx_external(mdx: &str, _node: &Node, fmt: &mut dyn Renderer) -> bool {
|
||||
// Parse inside of mdx: `external("text", "link")`
|
||||
let args = {
|
||||
let mdx = mdx
|
||||
.trim()
|
||||
.trim_start_matches('{')
|
||||
.trim_end_matches('}')
|
||||
.trim();
|
||||
|
||||
if !mdx.starts_with("external(") {
|
||||
return false;
|
||||
}
|
||||
|
||||
let skip = 9;
|
||||
let mut balance = 1;
|
||||
let mut end = skip;
|
||||
for i in mdx[skip..].bytes() {
|
||||
match i {
|
||||
b')' => balance -= 1,
|
||||
b'(' => balance += 1,
|
||||
_ => {}
|
||||
}
|
||||
|
||||
if balance == 0 {
|
||||
break;
|
||||
}
|
||||
|
||||
end += 1;
|
||||
}
|
||||
|
||||
if balance != 0 {
|
||||
return false;
|
||||
}
|
||||
|
||||
let args = mdx[skip..end].trim();
|
||||
let trail = mdx[end + 1..].trim();
|
||||
if !trail.is_empty() {
|
||||
return false;
|
||||
}
|
||||
|
||||
args
|
||||
};
|
||||
|
||||
// TODO: better parsing, handle , in string
|
||||
let mut split = args.split(",");
|
||||
|
||||
let title = match split.next() {
|
||||
Some(mut x) => {
|
||||
x = x.trim();
|
||||
|
||||
if &x[0..1] == "\"" {
|
||||
x = &x[1..]
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
|
||||
if &x[x.len() - 1..] == "\"" {
|
||||
x = &x[0..x.len() - 1]
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
|
||||
x
|
||||
}
|
||||
|
||||
None => return false,
|
||||
};
|
||||
|
||||
let link = match split.next() {
|
||||
Some(mut x) => {
|
||||
x = x.trim();
|
||||
|
||||
if &x[0..1] == "\"" {
|
||||
x = &x[1..]
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
|
||||
if &x[x.len() - 1..] == "\"" {
|
||||
x = &x[0..x.len() - 1]
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
|
||||
x
|
||||
}
|
||||
|
||||
None => return false,
|
||||
};
|
||||
|
||||
if split.next().is_some() {
|
||||
return false;
|
||||
}
|
||||
|
||||
fmt.text_raw(&FarLink(link, title).render().0);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//
|
||||
// MARK: yaml frontmatter
|
||||
//
|
||||
|
||||
@@ -31,4 +31,4 @@ A snippet of the [_Endless Sky_][es] map is below.
|
||||
|
||||
<br/>
|
||||
|
||||
<img alt="betalupi map" class="image" src="/assets/img/betalupi.png"></img>
|
||||
<img alt="betalupi map" src="/assets/img/betalupi.png"></img>
|
||||
|
||||
@@ -6,15 +6,12 @@ slug = "handouts"
|
||||
|
||||
# Mark's Handouts
|
||||
|
||||
[ORMC]: https://circles.math.ucla.edu/circles
|
||||
|
||||
This page lists all the handouts I've written for my classes at the
|
||||
{{external("ORMC", "https://circles.math.ucla.edu/circles")}},
|
||||
This page lists all the handouts I've written for my classes at the [ORMC],
|
||||
arguably the best math circle in the western world. We teach students mathematics
|
||||
far beyond the regular school curriculum, much like
|
||||
{{external("AOPS", "https://artofproblemsolving.com")}}
|
||||
and the
|
||||
{{external("BMC", "https://mathcircle.berkeley.edu")}}
|
||||
|
||||
far beyond the regular school curriculum, much like [AOPS](https://artofproblemsolving.com)
|
||||
and the [BMC](https://mathcircle.berkeley.edu).
|
||||
|
||||
<br></br>
|
||||
|
||||
@@ -36,10 +33,8 @@ are written with this in mind.\
|
||||
I do not expect the average student to finish all problems during this two-hour session.
|
||||
If the class finishes early, the lesson is either too short or too easy.
|
||||
|
||||
The sources for all these handouts are available
|
||||
{{external("here", "https://git.betalupi.com/mark/handouts")}} \
|
||||
Some are written in LaTeX, some are in
|
||||
{{external("Typst", "https://typst.app")}}. \
|
||||
The sources for all these handouts are available [here](https://git.betalupi.com/mark/handouts).\
|
||||
Some are written in LaTeX, some are in [Typst](https://typst.app). \
|
||||
The latter is vastly superior.
|
||||
|
||||
<br></br>
|
||||
|
||||
Reference in New Issue
Block a user