vertically aligned code comments
This commit is contained in:
parent
ee0a95974b
commit
408b984266
6 changed files with 83 additions and 12 deletions
|
@ -2,6 +2,7 @@
|
|||
//! Made concrete to avoid generic hell, with added treehouse-specific features.
|
||||
|
||||
use std::fmt::Write;
|
||||
use std::mem;
|
||||
use std::ops::Range;
|
||||
|
||||
use codespan_reporting::diagnostic::Diagnostic;
|
||||
|
@ -42,6 +43,7 @@ impl Renderer<'_> {
|
|||
renderer: self,
|
||||
raw: Raw::None,
|
||||
code_block: None,
|
||||
code_block_text: String::new(),
|
||||
img_alt_text: 0,
|
||||
list_tightness: vec![],
|
||||
not_first_line: false,
|
||||
|
@ -88,6 +90,7 @@ struct Writer<'a> {
|
|||
|
||||
raw: Raw,
|
||||
code_block: Option<CodeBlock<'a>>,
|
||||
code_block_text: String,
|
||||
img_alt_text: usize,
|
||||
list_tightness: Vec<bool>,
|
||||
not_first_line: bool,
|
||||
|
@ -445,6 +448,17 @@ impl<'a> Writer<'a> {
|
|||
Container::CodeBlock { language } => {
|
||||
let code_block = self.code_block.take().unwrap();
|
||||
|
||||
let rendered =
|
||||
if let Some(syntax) = self.renderer.config.syntaxes.get(*language) {
|
||||
let mut rendered = String::new();
|
||||
highlight(&mut rendered, syntax, &self.code_block_text);
|
||||
self.code_block_text.clear();
|
||||
rendered
|
||||
} else {
|
||||
mem::take(&mut self.code_block_text)
|
||||
};
|
||||
|
||||
out.push_str(&rendered);
|
||||
out.push_str(match &code_block.kind {
|
||||
CodeBlockKind::PlainText | CodeBlockKind::SyntaxHighlight => {
|
||||
"</code></pre>"
|
||||
|
@ -507,11 +521,8 @@ impl<'a> Writer<'a> {
|
|||
Event::Str(s) => match self.raw {
|
||||
Raw::None if self.img_alt_text > 0 => write_attr(s, out),
|
||||
Raw::None => {
|
||||
let syntax = self.code_block.as_ref().and_then(|code_block| {
|
||||
self.renderer.config.syntaxes.get(code_block.language)
|
||||
});
|
||||
if let Some(syntax) = syntax {
|
||||
highlight(out, syntax, s);
|
||||
if self.code_block.is_some() {
|
||||
self.code_block_text.push_str(s);
|
||||
} else {
|
||||
write_text(s, out);
|
||||
}
|
||||
|
|
|
@ -15,6 +15,8 @@ use std::{collections::HashMap, fmt::Write};
|
|||
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::html::highlight::tokenize::Token;
|
||||
|
||||
use self::compiled::CompiledSyntax;
|
||||
|
||||
use super::EscapeHtml;
|
||||
|
@ -82,13 +84,59 @@ pub struct Keyword {
|
|||
pub only_replaces: Option<String>,
|
||||
}
|
||||
|
||||
pub fn highlight(out: &mut String, syntax: &CompiledSyntax, code: &str) {
|
||||
let tokens = syntax.tokenize(code);
|
||||
fn write_tokens(
|
||||
out: &mut String,
|
||||
syntax: &CompiledSyntax,
|
||||
code: &str,
|
||||
tokens: impl Iterator<Item = Token>,
|
||||
) {
|
||||
for token in tokens {
|
||||
let str = &code[token.range.clone()];
|
||||
out.push_str("<span class=\"");
|
||||
_ = write!(out, "{}", EscapeHtml(&syntax.token_names[token.id]));
|
||||
out.push_str("\">");
|
||||
_ = write!(out, "{}", EscapeHtml(&code[token.range]));
|
||||
_ = write!(out, "{}", EscapeHtml(str));
|
||||
out.push_str("</span>");
|
||||
}
|
||||
}
|
||||
|
||||
pub fn highlight(out: &mut String, syntax: &CompiledSyntax, code: &str) {
|
||||
let tokens = syntax.tokenize(code);
|
||||
let mut line = vec![];
|
||||
|
||||
let mut in_columns = false;
|
||||
for token in tokens {
|
||||
let str = &code[token.range.clone()];
|
||||
line.push(token);
|
||||
|
||||
if str.ends_with('\n') {
|
||||
let line_comment = if line.last().is_some_and(|token| {
|
||||
Some(token.id) == syntax.comment_token_id
|
||||
&& code[token.range.clone()].ends_with('\n')
|
||||
}) {
|
||||
line.pop()
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
if let Some(line_comment) = line_comment {
|
||||
if !in_columns {
|
||||
out.push_str("<th-comment-columns>");
|
||||
in_columns = true;
|
||||
}
|
||||
|
||||
out.push_str("<span>");
|
||||
write_tokens(out, syntax, code, line.drain(..));
|
||||
out.push_str("</span>");
|
||||
write_tokens(out, syntax, code, [line_comment].into_iter());
|
||||
} else {
|
||||
if in_columns {
|
||||
out.push_str("</th-comment-columns>");
|
||||
in_columns = false;
|
||||
}
|
||||
|
||||
write_tokens(out, syntax, code, line.drain(..));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -17,6 +17,9 @@ pub struct CompiledSyntax {
|
|||
|
||||
pub patterns: Vec<CompiledPattern>,
|
||||
pub keywords: HashMap<String, CompiledKeyword>,
|
||||
|
||||
/// If there is a token named "comment", this is its ID.
|
||||
pub comment_token_id: Option<TokenId>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
|
@ -111,9 +114,12 @@ pub fn compile_syntax(syntax: &Syntax) -> CompiledSyntax {
|
|||
})
|
||||
.collect();
|
||||
|
||||
let comment_token_id = token_names.iter().position(|name| name == "comment");
|
||||
|
||||
CompiledSyntax {
|
||||
token_names,
|
||||
patterns,
|
||||
keywords,
|
||||
comment_token_id,
|
||||
}
|
||||
}
|
||||
|
|
|
@ -75,7 +75,7 @@ main.doc {
|
|||
--recursive-wght: 500;
|
||||
--recursive-mono: 0.5; /* You didn't expect a proportional font being used for code, did you. */
|
||||
font-size: 95%;
|
||||
tab-size: 3;
|
||||
tab-size: 3ch;
|
||||
}
|
||||
|
||||
&.monospaced code {
|
||||
|
@ -160,7 +160,7 @@ main.doc {
|
|||
& code {
|
||||
--recursive-wght: 520;
|
||||
font-size: 90%;
|
||||
tab-size: 2;
|
||||
tab-size: 2ch;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1198,6 +1198,11 @@ th-literate-program[data-mode="output"] {
|
|||
}
|
||||
}
|
||||
|
||||
.th-syntax-highlighting th-comment-columns {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(2, max-content);
|
||||
}
|
||||
|
||||
.th-syntax-highlighting {
|
||||
& .export {
|
||||
text-decoration: underline dotted;
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"patterns": [
|
||||
{ "regex": "\\/\\/.*", "is": "comment" },
|
||||
{ "regex": "\\/\\/.*\\n?", "is": "comment" },
|
||||
{
|
||||
"regex": "\\/\\*.*?\\*\\/",
|
||||
"flags": ["dotMatchesNewline"],
|
||||
|
@ -37,7 +37,8 @@
|
|||
}
|
||||
},
|
||||
{ "regex": "[a-zA-Z_][a-zA-Z0-9_]*", "is": "identifier" },
|
||||
{ "regex": "'[a-zA-Z_][a-zA-Z0-9_]*", "is": "literal" }
|
||||
{ "regex": "'[a-zA-Z_][a-zA-Z0-9_]*", "is": "literal" },
|
||||
{ "regex": "\n", "is": "" }
|
||||
],
|
||||
"keywords": {
|
||||
"_": { "into": "keyword1" },
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue