Skip to content

Commit

Permalink
Markdown render challenge descriptions (Closes mousetail#21)
Browse files Browse the repository at this point in the history
  • Loading branch information
mousetail committed Oct 27, 2024
1 parent 53bb61b commit 01dce14
Show file tree
Hide file tree
Showing 10 changed files with 413 additions and 27 deletions.
368 changes: 347 additions & 21 deletions Cargo.lock

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion lang-runner/src/run.rs
Original file line number Diff line number Diff line change
Expand Up @@ -171,7 +171,7 @@ async fn run_lang(
}

let data = serde_json::to_string(&RunnerInput { lang, code, judge })
.map_err(|e| RunProcessError::SerializationFailed(e))?;
.map_err(RunProcessError::SerializationFailed)?;
stdin.write_all(data.as_bytes()).await?;

let output = child.output().await?;
Expand Down
1 change: 1 addition & 0 deletions main-server/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -22,3 +22,4 @@ oauth2 = "5.0.0-rc.1"
tower-sessions = "0.13.0"
tower-http = { version = "0.6.1", features = ["catch-panic", "fs"] }
serde_json = "1.0.128"
markdown-it = "0.6.1"
6 changes: 5 additions & 1 deletion main-server/src/auto_output_format.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,10 @@ use serde::Deserialize;
use serde::{de::DeserializeOwned, Serialize};
use tera::{escape_html, to_value, Context, Tera, Value};

use crate::models::account::Account;
use crate::{
markdown::MarkdownFilter,
models::account::Account,
};

#[derive(Serialize)]
pub struct HtmlContext {
Expand Down Expand Up @@ -214,6 +217,7 @@ impl<T: Serialize> AutoOutputFormat<T> {
tera.autoescape_on(vec![".html.jinja", ".xml.jinja", ".html", ".xml"]);
tera.register_function("languages", get_langs);
tera.register_function("modules", load_assets);
tera.register_filter("markdown", MarkdownFilter);
tera
})
});
Expand Down
2 changes: 1 addition & 1 deletion main-server/src/controllers/challenges.rs
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,7 @@ pub async fn new_challenge(
)
.fetch_one(&pool)
.await
.map_err(|e| Error::DatabaseError(e))?;
.map_err(Error::DatabaseError)?;

Ok(Redirect::temporary(&format!("/challenge/{row}")).into_response())
}
Expand Down
1 change: 1 addition & 0 deletions main-server/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ mod auto_output_format;
mod controllers;
mod error;
mod file_session_storage;
mod markdown;
mod models;
mod session;
mod test_solution;
Expand Down
51 changes: 51 additions & 0 deletions main-server/src/markdown.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
use std::cell::OnceCell;

use markdown_it::{plugins::cmark::inline::link::Link, MarkdownIt};
use tera::Filter;

thread_local! {
static MARKDOWN: OnceCell<MarkdownIt> = const { OnceCell::new() };
}

pub fn render_markdown(source: &str) -> String {
MARKDOWN.with(|markdown| {
let parser = markdown.get_or_init(|| {
let mut parser = markdown_it::MarkdownIt::new();
markdown_it::plugins::cmark::add(&mut parser);
markdown_it::plugins::extra::add(&mut parser);
parser
});

let mut ast = parser.parse(source);

// TODO: this is probbly better with a plugin
ast.walk_mut(|e, _index| {
if e.is::<Link>() {
e.attrs.push(("rel", "nofollow noopener".to_owned()));
}
});
ast.render()
})
}

pub struct MarkdownFilter;

impl Filter for MarkdownFilter {
fn is_safe(&self) -> bool {
true
}

fn filter(
&self,
value: &tera::Value,
args: &std::collections::HashMap<String, tera::Value>,
) -> tera::Result<tera::Value> {
if !args.is_empty() {
return Err(tera::Error::msg("The markdown function takes no arguments"));
}
let text = value
.as_str()
.ok_or_else(|| tera::Error::msg("Expected type to be a string"))?;
Ok(tera::Value::String(render_markdown(text)))
}
}
2 changes: 1 addition & 1 deletion main-server/src/models/challenge.rs
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ impl ChallengeWithAuthorInfo {
.bind(id)
.fetch_optional(pool)
.await
.map_err(|e| Error::DatabaseError(e))?;
.map_err(Error::DatabaseError)?;

Ok(challenge)
}
Expand Down
1 change: 1 addition & 0 deletions templates/base/base.html.jinja
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
<meta name="description" value="Yet Another Golf Site" />
<meta name="keywords" value="code-golf" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<meta charset="UTF-8" />
{% endblock head %}

{% block scripts %}
Expand Down
6 changes: 4 additions & 2 deletions templates/challenge.html.jinja
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,16 @@
<h1>{{ object.challenge.challenge.name }}</h1>
<p>
A challenge by
<img src="{{ object.challenge.author_avatar }}&size=18" alt="{{ object.challenge.author_name }} avatar" height="18" />
<img src="{{ object.challenge.author_avatar }}&size=18"
alt="{{ object.challenge.author_name }} avatar"
height="18" />
{{ object.challenge.author_name }}
<a href="/challenge/{{ object.challenge.id }}">Edit</a>
</p>
<div class="two-column-descripion">
<div>
<h3>Descripion</h3>
{{ object.challenge.challenge.description }}
{{ object.challenge.challenge.description | markdown }}
</div>
<div class="leaderboard">
<h3>Leaderboard</h3>
Expand Down

0 comments on commit 01dce14

Please sign in to comment.