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