Skip to main content

max / makenotwork

3.5 KB · 89 lines History Blame Raw
1 //! The Askama entry point for [`quasi_router::Node::Code`].
2 //!
3 //! [`super::carousel`]'s shape and its reasoning: glue between a template's own
4 //! type and the vocabulary's, so a page that is still Askama draws the same
5 //! markup a described screen would. Called from `routes/git/browsing.rs` while
6 //! `templates/pages/git/file.html` is still a template.
7 //!
8 //! # Why this exists rather than the page just being converted
9 //!
10 //! The file view is one of three git pages that need the heavy form, and all
11 //! three sit behind `partials/git_nav.html` and `partials/git_notes_panel.html`,
12 //! which eight other git templates share. Converting the page means converting
13 //! the nav, which means converting the eight, which is the whole source-browser
14 //! pass rather than this one.
15 //!
16 //! What could not wait is the seam: `SyntaxHighlighter` stopped emitting HTML on
17 //! 2026-09-02 (quasi `19d7602d`), so something has to turn runs into markup, and
18 //! it should be the renderer rather than a second emitter here. When the page is
19 //! described this file goes and the screen carries `Node::Code` directly.
20
21 use quasi_router::Node;
22 use quasi_router::screen::Lexeme;
23
24 /// One line of classified source, as markup for a template to drop in.
25 ///
26 /// Inline, because a line of a file sits in a table cell beside its number:
27 /// the block form owns its own lines and the table is what holds these.
28 #[must_use]
29 pub fn line_html(runs: &[Lexeme], language: Option<&str>) -> String {
30 use quasi_axum::Serves as _;
31
32 // No shell: this lands inside a document Askama already built, which is
33 // what `fragment` is for.
34 quasi_webview::Webview::new().fragment(&Node::Code {
35 runs: runs.to_vec(),
36 language: language.map(str::to_owned),
37 inline: true,
38 })
39 }
40
41 #[cfg(test)]
42 mod tests {
43 use super::*;
44 use makeover_layout::Syntax;
45
46 /// The classification reaches the markup, and a plain run stays bare text
47 /// rather than becoming a span that says "ordinary".
48 #[test]
49 fn a_classified_line_arrives_whole() {
50 let html = line_html(
51 &[
52 Lexeme::new("fn", Syntax::Keyword),
53 Lexeme::plain(" main() {}"),
54 ],
55 Some("rust"),
56 );
57 assert!(html.contains("lex-keyword"), "{html}");
58 assert!(html.contains(" main() {}"), "{html}");
59 assert_eq!(html.matches("<span").count(), 1, "{html}");
60 }
61
62 /// A line the parser could not classify is still the line, which is the
63 /// degradation the whole design prefers: never lose a character to lose a
64 /// colour.
65 #[test]
66 fn an_unclassified_line_is_still_the_line() {
67 let html = line_html(&[Lexeme::plain("hello world")], None);
68 assert!(html.contains("hello world"), "{html}");
69 assert!(!html.contains("<span"), "{html}");
70 }
71
72 /// A blank line renders as an empty cell rather than vanishing: a file view
73 /// that dropped one would renumber every line under it.
74 #[test]
75 fn a_blank_line_still_renders() {
76 let html = line_html(&[], None);
77 assert!(html.contains("<code"), "{html}");
78 }
79
80 /// Source is reader input by the time it reaches here, and it goes into a
81 /// document. The renderer escapes it; this is the test that says so.
82 #[test]
83 fn source_cannot_smuggle_markup() {
84 let html = line_html(&[Lexeme::plain("<script>alert(1)</script>")], None);
85 assert!(!html.contains("<script>"), "{html}");
86 assert!(html.contains("&lt;script&gt;"), "{html}");
87 }
88 }
89