Merge branch 'main' of github.com:liquidev/treehouse

This commit is contained in:
りき萌 2024-03-31 18:51:14 +02:00
commit 91e5ed3eba
19 changed files with 785 additions and 17 deletions

View file

@ -1,4 +1,4 @@
use std::ops::Range;
use std::{ffi::OsStr, ops::Range};
use anyhow::Context;
use treehouse_format::ast::Branch;
@ -154,7 +154,7 @@ pub fn fix_file_cli(fix_args: FixArgs) -> anyhow::Result<()> {
pub fn fix_all_cli(fix_all_args: FixAllArgs, paths: &Paths<'_>) -> anyhow::Result<()> {
for entry in WalkDir::new(paths.content_dir) {
let entry = entry?;
if entry.file_type().is_file() {
if entry.file_type().is_file() && entry.path().extension() == Some(OsStr::new("tree")) {
let file = std::fs::read_to_string(entry.path())
.with_context(|| format!("cannot read file to fix: {:?}", entry.path()))?;
let utf8_filename = entry.path().to_string_lossy();

View file

@ -291,6 +291,7 @@ impl Generator {
treehouse,
config,
&mut config_derived_data,
paths,
parsed_tree.file_id,
&roots.branches,
);

View file

@ -86,6 +86,7 @@ fn get_content_type(path: &str) -> Option<&'static str> {
_ if path.ends_with(".html") => Some("text/html"),
_ if path.ends_with(".js") => Some("text/javascript"),
_ if path.ends_with(".woff2") => Some("font/woff2"),
_ if path.ends_with(".svg") => Some("image/svg+xml"),
_ => None,
}
}

View file

@ -1,9 +1,10 @@
use std::fmt::Write;
use std::{borrow::Cow, fmt::Write};
use pulldown_cmark::{BrokenLink, LinkType};
use treehouse_format::pull::BranchKind;
use crate::{
cli::Paths,
config::{Config, ConfigDerivedData},
html::EscapeAttribute,
state::{FileId, Treehouse},
@ -20,6 +21,7 @@ pub fn branch_to_html(
treehouse: &mut Treehouse,
config: &Config,
config_derived_data: &mut ConfigDerivedData,
paths: &Paths<'_>,
file_id: FileId,
branch_id: SemaBranchId,
) {
@ -49,6 +51,11 @@ pub fn branch_to_html(
} else {
"b"
};
let component = if !branch.attributes.cast.is_empty() {
Cow::Owned(format!("{component} {}", branch.attributes.cast))
} else {
Cow::Borrowed(component)
};
let linked_branch = if let Content::Link(link) = &branch.attributes.content {
format!(" data-th-link=\"{}\"", EscapeHtml(link))
@ -62,9 +69,19 @@ pub fn branch_to_html(
""
};
let mut data_attributes = String::new();
for (key, value) in &branch.attributes.data {
write!(
data_attributes,
" data-{key}=\"{}\"",
EscapeAttribute(value)
)
.unwrap();
}
write!(
s,
"<li data-cast=\"{component}\" class=\"{class}\" id=\"{}\"{linked_branch}{do_not_persist}>",
"<li data-cast=\"{component}\" class=\"{class}\" id=\"{}\"{linked_branch}{do_not_persist}{data_attributes}>",
EscapeAttribute(&branch.html_id)
)
.unwrap();
@ -136,7 +153,7 @@ pub fn branch_to_html(
}
};
if branch.attributes.template {
final_markdown = mini_template::render(config, treehouse, &final_markdown);
final_markdown = mini_template::render(config, treehouse, paths, &final_markdown);
}
let markdown_parser = pulldown_cmark::Parser::new_with_broken_link_callback(
&final_markdown,
@ -204,7 +221,15 @@ pub fn branch_to_html(
let num_children = branch.children.len();
for i in 0..num_children {
let child_id = treehouse.tree.branch(branch_id).children[i];
branch_to_html(s, treehouse, config, config_derived_data, file_id, child_id);
branch_to_html(
s,
treehouse,
config,
config_derived_data,
paths,
file_id,
child_id,
);
}
s.push_str("</ul>");
}
@ -221,12 +246,21 @@ pub fn branches_to_html(
treehouse: &mut Treehouse,
config: &Config,
config_derived_data: &mut ConfigDerivedData,
paths: &Paths<'_>,
file_id: FileId,
branches: &[SemaBranchId],
) {
s.push_str("<ul>");
for &child in branches {
branch_to_html(s, treehouse, config, config_derived_data, file_id, child);
branch_to_html(
s,
treehouse,
config,
config_derived_data,
paths,
file_id,
child,
);
}
s.push_str("</ul>");
}

View file

@ -1,3 +1,5 @@
use std::collections::HashMap;
use serde::{Deserialize, Serialize};
/// Top-level `%%` root attributes.
@ -79,6 +81,14 @@ pub struct Attributes {
/// debug mode.
#[serde(default)]
pub stage: Stage,
/// List of extra spells to cast on the branch.
#[serde(default)]
pub cast: String,
/// List of extra `data` attributes to add to the block.
#[serde(default)]
pub data: HashMap<String, String>,
}
/// Controls for block content presentation.

View file

@ -8,7 +8,7 @@ use std::ops::Range;
use pulldown_cmark::escape::escape_html;
use crate::{config::Config, state::Treehouse};
use crate::{cli::Paths, config::Config, state::Treehouse};
struct Lexer<'a> {
input: &'a str,
@ -149,7 +149,7 @@ impl<'a> Renderer<'a> {
self.output.push_str(&self.lexer.input[token.range.clone()]);
}
fn render(&mut self, config: &Config, treehouse: &Treehouse) {
fn render(&mut self, config: &Config, treehouse: &Treehouse, paths: &Paths<'_>) {
let kind_of = |token: &Token| token.kind;
while let Some(token) = self.lexer.next() {
@ -166,6 +166,7 @@ impl<'a> Renderer<'a> {
match Self::render_template(
config,
treehouse,
paths,
self.lexer.input[inside.as_ref().unwrap().range.clone()].trim(),
) {
Ok(s) => match escaping {
@ -192,22 +193,24 @@ impl<'a> Renderer<'a> {
fn render_template(
config: &Config,
_treehouse: &Treehouse,
paths: &Paths<'_>,
template: &str,
) -> Result<String, InvalidTemplate> {
let (function, arguments) = template.split_once(' ').unwrap_or((template, ""));
match function {
"pic" => Ok(config.pic_url(arguments)),
"c++" => Ok("<script>alert(1)</script>".into()),
"include_static" => std::fs::read_to_string(paths.static_dir.join(arguments))
.map_err(|_| InvalidTemplate),
_ => Err(InvalidTemplate),
}
}
}
pub fn render(config: &Config, treehouse: &Treehouse, input: &str) -> String {
pub fn render(config: &Config, treehouse: &Treehouse, paths: &Paths<'_>, input: &str) -> String {
let mut renderer = Renderer {
lexer: Lexer::new(input),
output: String::new(),
};
renderer.render(config, treehouse);
renderer.render(config, treehouse, paths);
renderer.output
}