~aw/patches

This thread contains a patchset. You're looking at the original emails, but you may wish to use the patch review UI. Review patch
3 2

[PATCH mygit v2 1/3] use Solarized colour scheme, dark mode

Johann Galle
Details
Message ID
<20210328214529.1369-1-johann@qwertqwefsday.eu>
DKIM signature
pass
Download raw message
Patch: +130 -44
From: Johann150 <johann@qwertqwefsday.eu>

To be able to use dark mode in the form of CSS's prefers-color-scheme,
syntect had to be switched to output classes instead of hard coded colours.

This is a bit problematic as seen in the changes on the stylesheet. Some of
the CSS classes could probably be merged, but this is a nontrivial problem.

Because of the increased size, we should enable Caching for the stylesheet,
which tide's serve_file does not automatically do.
---
 src/main.rs                |  31 ++++----
 templates/static/style.css | 143 +++++++++++++++++++++++++++++--------
 2 files changed, 130 insertions(+), 44 deletions(-)

diff --git a/src/main.rs b/src/main.rs
index c5cb8cd..9964bfd 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -6,7 +6,6 @@ use serde::Deserialize;
use std::fs;
use std::path::Path;
use std::str;
use syntect::highlighting::ThemeSet;
use syntect::parsing::SyntaxSet;
use tide::Request;

@@ -392,17 +391,16 @@ async fn repo_file(req: Request<()>) -> tide::Result {
    // TODO make sure I am escaping html properly here
    // TODO allow disabling of syntax highlighting
    // TODO -- dont pull in memory, use iterators if possible
    let syntax_set = SyntaxSet::load_defaults_nonewlines();
    let syntax_set = SyntaxSet::load_defaults_newlines();
    let extension = path
        .extension()
        .and_then(std::ffi::OsStr::to_str)
        .unwrap_or_default();
    let syntax_reference = syntax_set
    let syntax = syntax_set
        .find_syntax_by_extension(extension)
        .unwrap_or_else(|| syntax_set.find_syntax_plain_text());
    let ts = ThemeSet::load_defaults();
    let theme = &ts.themes["InspiredGitHub"]; // TODO make customizable
    let tmpl = match tree_entry.to_object(&repo)?.into_tree() {
	// this is a subtree
        Ok(tree) => RepoTreeTemplate {
            repo: &repo,
            tree,
@@ -410,21 +408,22 @@ async fn repo_file(req: Request<()>) -> tide::Result {
            spec: &spec,
        }
        .into(),
        // this is not a subtree, so it should be a blob i.e. file
        Err(tree_obj) => {
            use syntect::{html::{ClassedHTMLGenerator, ClassStyle}, util::LinesWithEndings};

            // get file contents from git object
            let file_string = str::from_utf8(tree_obj.as_blob().unwrap().content())?;
            let mut highlighter = syntect::easy::HighlightLines::new(&syntax_reference, &theme);
            let (mut output, bg) = syntect::html::start_highlighted_html_snippet(&theme);
            for (n, line) in syntect::util::LinesWithEndings::from(file_string).enumerate() {
                let regions = highlighter.highlight(line, &syntax_set);
            // create a highlighter that uses CSS classes so we can use prefers-color-scheme
            let mut highlighter = ClassedHTMLGenerator::new_with_class_style(&syntax, &syntax_set, ClassStyle::SpacedPrefixed{prefix:"code"});
            LinesWithEndings::from(file_string).for_each(|line| highlighter.parse_html_for_line_which_includes_newline(line));

            let mut output = String::from("<pre>\n");
            for (n, line) in highlighter.finalize().lines().enumerate() {
                output.push_str(&format!(
                    "<a href='#L{0}' id='L{0}' class='line'>{0}</a>",
                    n + 1
                    "<a href='#L{0}' id='L{0}' class='line'>{0}</a>{1}\n",
                    n + 1, line
                ));
                syntect::html::append_highlighted_html_for_styled_line(
                    &regions[..],
                    syntect::html::IncludeBackground::IfDifferent(bg),
                    &mut output,
                );
            }
            output.push_str("</pre>\n");

diff --git a/templates/static/style.css b/templates/static/style.css
index 77e6c4a..0399b3c 100644
--- a/templates/static/style.css
+++ b/templates/static/style.css
@@ -5,7 +5,6 @@ body {
    margin: auto;
    font-family: "Archivo", Helvetica, Arial, Sans-serif;
    word-wrap: break-word;
    background-color: white;
    line-height: 1.5;
}

@@ -13,10 +12,6 @@ body {
    max-width: 80ch;
}

a:visited {
    color: blue;
}

.repo-last-updated {
    font-style: italic;
}
@@ -53,10 +48,6 @@ a:visited {
    user-select: none; /* Non-prefixed version, currently */
}

:target {
    background: #FFFACD;
}

.clone-url {
    font-family: "Roboto Mono", monospace;
    -webkit-user-select: all; /* Safari */
@@ -66,31 +57,24 @@ a:visited {
hr.thin {
    border: 0;
    height: 0;
    /* top border for light color scheme */
    border-top: 1px solid rgba(0, 0, 0, 0.1);
    /* bottom border for dark color scheme */
    border-bottom: 1px solid rgba(255, 255, 255, 0.3);
    border-top: 1px solid;
}

h1, h2, h3 {
     margin: 0px;
     margin: 0;
}

pre {
    line-height: 1.2;
    font-size: 1rem;
    overflow: auto;
    margin: 0px;
    padding: 0px;
}

tr:hover {
    background-color: #f0f0f0;
    margin: 0;
    padding: 0;
}

td {
    white-space: nowrap;
    margin: 0px;
    margin: 0;
    border-style:hidden;
    padding: 0;
}
@@ -114,17 +98,120 @@ table.core {
    }
}

/* for code highlighting */

.markup.italic {
    font-style: italic;
}

.markup.bold {
    font-weight: bold;
}

/*
color scheme: Solarized
most of these classes are used by syntect

Light is the default color scheme, not using a media query for browser support.
*/
/* TODO simplify, e.g. merge CSS classes. */

.storage.modifier, .meta.scope.for-in-loop.shell, .variable.other.loop.shell, .meta.scope.case-block.shell, .meta.scope.case-body.shell {
    color: #586e75; /* Solarized base01 */
}

body, .code, .keyword.operator.comparison, .keyword.operator.assignment, .keyword.operator.arithmetic, .meta.brace.round, .meta.brace.curly, .punctuation.section, .punctuation.section.block, .punctuation.definition.parameters, .punctuation.section.group, .meta.selector.css, .text.html.basic, .meta.tag.other.html, .text.html.basic, .meta.tag.any.html, .text.html.basic, .meta.tag.block.any, .text.html.basic, .meta.tag.inline.any, .text.html.basic, .meta.tag.structure.any.html, .text.html.basic, .source.js.embedded.html, .punctuation.separator.key-value.html, .variable.other.readwrite.js, .variable.other.object.js, .variable.other.constant.js {
    color: #657b83; /* Solarized base00 */
}

hr.thin {
    border-top-color: #657b83; /* Solarized base00 */
}

.punctuation.definition.string {
    color: #839496; /* Solarized base0 */
}

.comment, .meta.documentation, .punctuation.definition.tag.html, .punctuation.definition.tag.begin, .punctuation.definition.tag.end, .meta.diff, .meta.diff.header, .constant.numeric.line-number.find-in-files {
    color: #93a1a1; /* Solarized base1 */
}

:target, tr:hover, .links, .meta.paragraph.markdown, .meta.dummy.line-break {
    background-color: #eee8d5; /* Solarized base2 */
}

body, .code {
    background-color: #fdf6e3; /* Solarized base3 */
}

.variable.function, .keyword.control.class, .entity.name, .entity.name.class, .entity.name.type.class, .entity.other.attribute-name, .entity.name.function, .constant, .constant.language, .meta.preprocessor, .support.constant.color, .invalid.deprecated.color.w3c-non-standard-color-name.scss, .entity.name.tag.css, .entity.name.tag.scss, .source.less, .keyword.control.html.elements, .source.sass, .keyword.control.untitled, .entity.other.attribute-name.class, .entity.other.attribute-name.id, .text.html.basic, .entity.other.attribute-name.html, .meta.tag.xml, .entity.other.attribute-name, .variable.other.constant.ruby, .meta.array, .support.function.construct.php, .meta.group.braces.tex, .string.other.math.tex, .string.other.math.tex, .support.type.exception.python, .markup.heading, .punctuation.definition.heading.markdown, .markup.changed.git_gutter {
    color: #b58900; /* Solarized yellow */
}

.meta.import, .keyword, .keyword.control.import, .keyword.control.import.from, .keyword.other.import, .keyword.control.at-rule.include, .keyword.control.at-rule.import, .entity.name.section, .constant.character, .constant.other, .support.type.exception, .keyword.other.special-method, .keyword.other.special-method.ruby, .entity.name.function.preprocessor.c, .meta.preprocessor.c.include, .meta.preprocessor.macro.c, .variable.parameter.function.latex, .support.function.section.latex {
    color: #cb4b16; /* Solarized orange */
}

.constant.character.escape, .support.function.construct, .keyword.other.new, .punctuation.separator.continuation, .other.package.exclude, .other.remove, .punctuation.section.group.tex, .punctuation.definition.arguments.begin.latex, .punctuation.definition.arguments.end.latex, .punctuation.definition.arguments.latex, .punctuation.definition.constant.math.tex, .punctuation.definition.string.begin.tex, .punctuation.definition.string.end.tex, .variable.parameter.definition.label.latex, .punctuation.definition.logical-expression.shell, .markup.deleted, .markup.deleted.git_gutter {
    color: #dc322f; /* Solarized red */
}

.variable.language {
    color: #d33682; /* Solarized magenta */
}

.constant.numeric, .constant.other.reference.link.markdown {
    color: #6c71c4; /* Solarized violet */
}

a, .variable, .entity.other.inherited-class, .entity.name.tag, .storage.type, .meta.brace.square, .punctuation.section.brackets, .entity.other.attribute-name.pseudo-element, .entity.other.attribute-name.tag.pseudo-element, .entity.other.attribute-name.pseudo-class, .entity.other.attribute-name.tag.pseudo-class, .support.function.perl, .meta.diff.range {
    color: #268bd2; /* Solarized blue */
}

.string, .string.regexp, .string.quoted.double, .string.quoted.single, .constant.other.symbol.ruby, .meta.preprocessor.c.include, .string.quoted.other.lt-gt.include.c, .meta.preprocessor.c.include, .punctuation.definition.string.begin.c, .meta.preprocessor.c.include, .punctuation.definition.string.end.c, .other.add, .text.tex.latex, .constant.other.math.tex, .constant.other.general.math.tex, .constant.other.general.math.tex, .constant.character.math.tex, .keyword.control.label.latex, .text.tex.latex, .constant.other.general.math.tex, .support.function.general.tex, .keyword.control.ref.latex, .markup.changed, .markup.underline.link.markdown, .meta.link.reference, .constant.other.reference.link.markdown, .entity.name.filename.find-in-files {
    color: #2aa198; /* Solarized cyan */
}

.keyword, .storage, .support, .support.type, .support.class, .punctuation.definition.variable, .support.function, .keyword.other.special-method.ruby, .support.function.be.latex, .storage.type.class.python, .storage.type.function.python, .storage.modifier.global.python, .storage.modifier.c++, .markup.inserted, .markup.quote, .markup.inserted.git_gutter {
    color: #859900; /* Solarized green */
}

.invalid {
    background-color: #ec9489;
}

/* Dark colour scheme for browsers that support it. */

@media (prefers-color-scheme: dark) {
    body {
        background: #212529;
        color: #f8f9fa;
    .storage.modifier, .meta.scope.for-in-loop.shell, .variable.other.loop.shell, .meta.scope.case-block.shell, .meta.scope.case-body.shell {
        color: #93a1a1; /* Solarized base1 */
    }

    body, .code, .keyword.operator.comparison, .keyword.operator.assignment, .keyword.operator.arithmetic, .meta.brace.round, .meta.brace.curly, .punctuation.section, .punctuation.section.block, .punctuation.definition.parameters, .punctuation.section.group, .meta.selector.css, .text.html.basic, .meta.tag.other.html, .text.html.basic, .meta.tag.any.html, .text.html.basic, .meta.tag.block.any, .text.html.basic, .meta.tag.inline.any, .text.html.basic, .meta.tag.structure.any.html, .text.html.basic, .source.js.embedded.html, .punctuation.separator.key-value.html, .variable.other.readwrite.js, .variable.other.object.js, .variable.other.constant.js {
        color: #839496; /* Solarized base0 */
    }

    hr.thin {
        border-top-color: #839496; /* Solarized base0 */
    }

    .punctuation.definition.string {
        color: #657b83; /* Solarized base00 */
    }

    .comment, .meta.documentation, .punctuation.definition.tag.html, .punctuation.definition.tag.begin, .punctuation.definition.tag.end, .meta.diff, .meta.diff.header, .constant.numeric.line-number.find-in-files {
        color: #586e75; /* Solarized base01 */
    }

    :target, tr:hover, .links, .meta.paragraph.markdown, .meta.dummy.line-break {
        background-color: #073642; /* Solarized base02 */
    }

    :target {
        background: #ffffff;
    body, .code {
        background-color: #002b36; /* Solarized base03 */
    }

    tr:hover {
        background-color: #0f0f0f;
    .invalid {
        background-color: #6e2e32;
    }
}
-- 
2.20.1

[PATCH mygit v2 2/3] format, add catchall error pages

Johann Galle
Details
Message ID
<20210328214529.1369-2-johann@qwertqwefsday.eu>
In-Reply-To
<20210328214529.1369-1-johann@qwertqwefsday.eu> (view parent)
DKIM signature
pass
Download raw message
Patch: +29 -8
From: Johann150 <johann@qwertqwefsday.eu>

---
 src/errorpage.rs |  8 ++++++++
 src/main.rs      | 29 +++++++++++++++++++++--------
 2 files changed, 29 insertions(+), 8 deletions(-)

diff --git a/src/errorpage.rs b/src/errorpage.rs
index 3df280f..a1436d3 100644
--- a/src/errorpage.rs
+++ b/src/errorpage.rs
@@ -24,6 +24,14 @@ impl<State: Clone + Send + Sync + 'static> Middleware<State> for ErrorToErrorpag
                message: err.into_inner().to_string(),
            }
            .into();
            if status == 405 {
                // The origin server MUST generate an Allow header field in
                // a 405 response containing a list of the target
                // resource's currently supported methods. - RFC 7231§6.5.5
                //
                // We only ever support GET requests.
                response.insert_header("Allow", "GET");
            }
            response.set_status(status);
        }

diff --git a/src/main.rs b/src/main.rs
index 9964bfd..2ba9f68 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -7,7 +7,7 @@ use std::fs;
use std::path::Path;
use std::str;
use syntect::parsing::SyntaxSet;
use tide::Request;
use tide::{Request, Response};

mod errorpage;

@@ -400,7 +400,7 @@ async fn repo_file(req: Request<()>) -> tide::Result {
        .find_syntax_by_extension(extension)
        .unwrap_or_else(|| syntax_set.find_syntax_plain_text());
    let tmpl = match tree_entry.to_object(&repo)?.into_tree() {
	// this is a subtree
        // this is a subtree
        Ok(tree) => RepoTreeTemplate {
            repo: &repo,
            tree,
@@ -410,19 +410,28 @@ async fn repo_file(req: Request<()>) -> tide::Result {
        .into(),
        // this is not a subtree, so it should be a blob i.e. file
        Err(tree_obj) => {
            use syntect::{html::{ClassedHTMLGenerator, ClassStyle}, util::LinesWithEndings};
            use syntect::{
                html::{ClassStyle, ClassedHTMLGenerator},
                util::LinesWithEndings,
            };

            // get file contents from git object
            let file_string = str::from_utf8(tree_obj.as_blob().unwrap().content())?;
            // create a highlighter that uses CSS classes so we can use prefers-color-scheme
            let mut highlighter = ClassedHTMLGenerator::new_with_class_style(&syntax, &syntax_set, ClassStyle::SpacedPrefixed{prefix:"code"});
            LinesWithEndings::from(file_string).for_each(|line| highlighter.parse_html_for_line_which_includes_newline(line));
            let mut highlighter = ClassedHTMLGenerator::new_with_class_style(
                &syntax,
                &syntax_set,
                ClassStyle::SpacedPrefixed { prefix: "code" },
            );
            LinesWithEndings::from(file_string)
                .for_each(|line| highlighter.parse_html_for_line_which_includes_newline(line));

            let mut output = String::from("<pre>\n");
            for (n, line) in highlighter.finalize().lines().enumerate() {
                output.push_str(&format!(
                    "<a href='#L{0}' id='L{0}' class='line'>{0}</a>{1}\n",
                    n + 1, line
                    n + 1,
                    line
                ));
            }
            output.push_str("</pre>\n");
@@ -551,10 +560,12 @@ async fn main() -> Result<(), std::io::Error> {
        .serve_file("templates/static/style.css")?; // TODO configurable
    app.at("/:repo_name").get(repo_home);
    app.at("/:repo_name/").get(repo_home);
    // git clone stuff -- handle thse urls

    // git clone stuff
    app.at("/:repo_name/info/refs").get(git_data);
    app.at("/:repo_name/HEAD").get(git_data);
    app.at("/:repo_name/objects/*obj").get(git_data);

    app.at("/:repo_name/commit/:commit").get(repo_commit);
    app.at("/:repo_name/refs").get(repo_refs);
    app.at("/:repo_name/log").get(repo_log);
@@ -563,8 +574,10 @@ async fn main() -> Result<(), std::io::Error> {
    app.at("/:repo_name/tree/:ref").get(repo_tree);
    app.at("/:repo_name/tree/:ref/item/*object_name")
        .get(repo_file);
    app.at("*")
        .get(|_| async { Result::<Response, tide::Error>::Err(tide::Error::from_str(404, "This page does not exist.")) })
        .all(|_| async { Result::<Response, tide::Error>::Err(tide::Error::from_str(405, "This method is not allowed.")) });
    // Raw files, patch files
    app.listen(format!("[::]:{}", CONFIG.port)).await?;
    // app.all 404
    Ok(())
}
-- 
2.20.1

[PATCH mygit v2 3/3] caching for static resources

Johann Galle
Details
Message ID
<20210328214529.1369-3-johann@qwertqwefsday.eu>
In-Reply-To
<20210328214529.1369-1-johann@qwertqwefsday.eu> (view parent)
DKIM signature
pass
Download raw message
Patch: +112 -23
From: Johann150 <johann@qwertqwefsday.eu>

---
 src/errorpage.rs |  29 +++++++++----
 src/main.rs      | 106 ++++++++++++++++++++++++++++++++++++++++-------
 2 files changed, 112 insertions(+), 23 deletions(-)

diff --git a/src/errorpage.rs b/src/errorpage.rs
index a1436d3..f6995f6 100644
--- a/src/errorpage.rs
+++ b/src/errorpage.rs
@@ -15,24 +15,35 @@ pub struct ErrorToErrorpage;
impl<State: Clone + Send + Sync + 'static> Middleware<State> for ErrorToErrorpage {
    async fn handle(&self, req: Request<State>, next: Next<'_, State>) -> tide::Result {
        let resource = req.url().path().to_string();
        let method = req.method();
        let mut response = next.run(req).await;
        if let Some(err) = response.take_error() {
            let status = err.status();
            response = ErrorTemplate {
                resource,
                status,
                message: err.into_inner().to_string(),

            if method == tide::http::Method::Head {
                // the server MUST NOT send a message body in the response
                // - RFC 7231 § 4.3.2
                response.take_body();
            } else {
                response = ErrorTemplate {
                    resource,
                    status,
                    message: err.into_inner().to_string(),
                }
                .into();
                response.set_status(status);
            }
            .into();

            if status == 405 {
                // The origin server MUST generate an Allow header field in
                // a 405 response containing a list of the target
                // resource's currently supported methods. - RFC 7231§6.5.5
                // resource's currently supported methods.
                // - RFC 7231 § 6.5.5
                //
                // We only ever support GET requests.
                response.insert_header("Allow", "GET");
                // We only ever support GET or HEAD requests.
                // tide adds support for HEAD automatically if we implement GET
                response.insert_header("Allow", "GET, HEAD");
            }
            response.set_status(status);
        }

        Ok(response)
diff --git a/src/main.rs b/src/main.rs
index 2ba9f68..5bee9fb 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -3,7 +3,8 @@ use askama::Template;
use git2::{Commit, Diff, DiffDelta, Reference, Repository, Tree, TreeEntry};
use once_cell::sync::Lazy;
use serde::Deserialize;
use std::fs;
use std::fs::{self, File};
use std::io::Read;
use std::path::Path;
use std::str;
use syntect::parsing::SyntaxSet;
@@ -468,10 +469,7 @@ async fn git_data(req: Request<()>) -> tide::Result {
            } else if !path.is_file() {
                // Either the requested resource does not exist or it is not
                // a file, i.e. a directory.
                Err(tide::Error::from_str(
                    404,
                    "The file you tried to access does not exist.",
                ))
                Err(tide::Error::from_str(404, "This page does not exist."))
            } else {
                // ok - inside the repo directory
                let mut resp = tide::Response::new(200);
@@ -488,6 +486,89 @@ async fn git_data(req: Request<()>) -> tide::Result {
    }
}

/// Serve a file from ./templates/static/
async fn static_resource(req: Request<()>) -> tide::Result {
    use tide::http::conditional::{IfModifiedSince, LastModified};

    // only use a File handle here because we might not need to load the file
    let file_mime_option = match req.url().path() {
        "/style.css" => Some((
            File::open("templates/static/style.css").unwrap(),
            tide::http::mime::CSS,
        )),
        "/robots.txt" => Some((
            File::open("templates/static/robots.txt").unwrap(),
            tide::http::mime::PLAIN,
        )),
        _ => None,
    };

    match file_mime_option {
        Some((mut file, mime)) => {
            let metadata = file.metadata().unwrap();
            let last_modified = metadata.modified().unwrap();

            let header = IfModifiedSince::from_headers(&req).unwrap();

            // check cache validating headers
            if matches!(header, Some(date) if IfModifiedSince::new(last_modified) <= date) {
                // the file has not changed
                let mut response = Response::new(304);
                response.set_content_type(mime);
                LastModified::new(last_modified).apply(&mut response);

                /*
                A server MAY send a Content-Length header field in a 304
                response to a conditional GET request; a server MUST NOT send
                Content-Length in such a response unless its field-value equals
                the decimal number of octets that would have been sent in the
                payload body of a 200 response to the same request.
                - RFC 7230 § 3.3.2
                */
                response.insert_header("Content-Length", metadata.len().to_string());

                return Ok(response);
            }

            let mut response = Response::new(200);

            match req.method() {
                tide::http::Method::Head => {
                    /*
                    A server MAY send a Content-Length header field in a
                    response to a HEAD request; a server MUST NOT send
                    Content-Length in such a response unless its field-value
                    equals the decimal number of octets that would have been
                    sent in the payload body of a response if the same request
                    had used the GET method.
                    - RFC 7230 § 3.3.2
                    */
                    response.insert_header(
                        "Content-Length",
                        file.metadata().unwrap().len().to_string(),
                    );
                }
                tide::http::Method::Get => {
                    // load the file from disk
                    let mut content = String::new();
                    file.read_to_string(&mut content).unwrap();
                    response.set_body(content);
                }
                _ => return Err(tide::Error::from_str(405, "")),
            }

            response.set_content_type(mime);
            LastModified::new(last_modified).apply(&mut response);
            Ok(response)
        }
        None if req.method() == tide::http::Method::Get => {
            Err(tide::Error::from_str(404, "This page does not exist."))
        }
        // issue a 405 error since this is used as the catchall
        None => Err(tide::Error::from_str(405, "")),
    }
}

mod filters {
    use super::*;

@@ -554,10 +635,7 @@ async fn main() -> Result<(), std::io::Error> {
    let mut app = tide::new();
    app.with(errorpage::ErrorToErrorpage);
    app.at("/").get(index);
    app.at("/robots.txt")
        .serve_file("templates/static/robots.txt")?; // TODO configurable
    app.at("/style.css")
        .serve_file("templates/static/style.css")?; // TODO configurable

    app.at("/:repo_name").get(repo_home);
    app.at("/:repo_name/").get(repo_home);

@@ -566,17 +644,17 @@ async fn main() -> Result<(), std::io::Error> {
    app.at("/:repo_name/HEAD").get(git_data);
    app.at("/:repo_name/objects/*obj").get(git_data);

    // web pages
    app.at("/:repo_name/commit/:commit").get(repo_commit);
    app.at("/:repo_name/refs").get(repo_refs);
    app.at("/:repo_name/log").get(repo_log);
    app.at("/:repo_name/log/:ref").get(repo_log); // ref optional
    app.at("/:repo_name/log/:ref").get(repo_log); // ref is optional
    app.at("/:repo_name/tree").get(repo_tree);
    app.at("/:repo_name/tree/:ref").get(repo_tree);
    app.at("/:repo_name/tree/:ref").get(repo_tree); // ref is optional
    app.at("/:repo_name/tree/:ref/item/*object_name")
        .get(repo_file);
    app.at("*")
        .get(|_| async { Result::<Response, tide::Error>::Err(tide::Error::from_str(404, "This page does not exist.")) })
        .all(|_| async { Result::<Response, tide::Error>::Err(tide::Error::from_str(405, "This method is not allowed.")) });

    app.at("*").all(static_resource);
    // Raw files, patch files
    app.listen(format!("[::]:{}", CONFIG.port)).await?;
    Ok(())
-- 
2.20.1
Details
Message ID
<CA9EG5VZM183.19Y7ZJAHLX0UE@debian-alex>
In-Reply-To
<20210328214529.1369-1-johann@qwertqwefsday.eu> (view parent)
DKIM signature
pass
Download raw message
Thanks again for your work, I've pushed these changes

Alex

On Sun Mar 28, 2021 at 2:45 PM PDT, Johann Galle wrote:
> From: Johann150 <johann@qwertqwefsday.eu>
>
> To be able to use dark mode in the form of CSS's prefers-color-scheme,
> syntect had to be switched to output classes instead of hard coded
> colours.
>
> This is a bit problematic as seen in the changes on the stylesheet. Some
> of
> the CSS classes could probably be merged, but this is a nontrivial
> problem.
>
> Because of the increased size, we should enable Caching for the
> stylesheet,
> which tide's serve_file does not automatically do.
> ---
> src/main.rs | 31 ++++----
> templates/static/style.css | 143 +++++++++++++++++++++++++++++--------
> 2 files changed, 130 insertions(+), 44 deletions(-)
>
> diff --git a/src/main.rs b/src/main.rs
> index c5cb8cd..9964bfd 100644
> --- a/src/main.rs
> +++ b/src/main.rs
> @@ -6,7 +6,6 @@ use serde::Deserialize;
> use std::fs;
> use std::path::Path;
> use std::str;
> -use syntect::highlighting::ThemeSet;
> use syntect::parsing::SyntaxSet;
> use tide::Request;
>  
> @@ -392,17 +391,16 @@ async fn repo_file(req: Request<()>) ->
> tide::Result {
> // TODO make sure I am escaping html properly here
> // TODO allow disabling of syntax highlighting
> // TODO -- dont pull in memory, use iterators if possible
> - let syntax_set = SyntaxSet::load_defaults_nonewlines();
> + let syntax_set = SyntaxSet::load_defaults_newlines();
> let extension = path
> .extension()
> .and_then(std::ffi::OsStr::to_str)
> .unwrap_or_default();
> - let syntax_reference = syntax_set
> + let syntax = syntax_set
> .find_syntax_by_extension(extension)
> .unwrap_or_else(|| syntax_set.find_syntax_plain_text());
> - let ts = ThemeSet::load_defaults();
> - let theme = &ts.themes["InspiredGitHub"]; // TODO make customizable
> let tmpl = match tree_entry.to_object(&repo)?.into_tree() {
> + // this is a subtree
> Ok(tree) => RepoTreeTemplate {
> repo: &repo,
> tree,
> @@ -410,21 +408,22 @@ async fn repo_file(req: Request<()>) ->
> tide::Result {
> spec: &spec,
> }
> .into(),
> + // this is not a subtree, so it should be a blob i.e. file
> Err(tree_obj) => {
> + use syntect::{html::{ClassedHTMLGenerator, ClassStyle},
> util::LinesWithEndings};
> +
> + // get file contents from git object
> let file_string =
> str::from_utf8(tree_obj.as_blob().unwrap().content())?;
> - let mut highlighter =
> syntect::easy::HighlightLines::new(&syntax_reference, &theme);
> - let (mut output, bg) =
> syntect::html::start_highlighted_html_snippet(&theme);
> - for (n, line) in
> syntect::util::LinesWithEndings::from(file_string).enumerate() {
> - let regions = highlighter.highlight(line, &syntax_set);
> + // create a highlighter that uses CSS classes so we can use
> prefers-color-scheme
> + let mut highlighter =
> ClassedHTMLGenerator::new_with_class_style(&syntax, &syntax_set,
> ClassStyle::SpacedPrefixed{prefix:"code"});
> + LinesWithEndings::from(file_string).for_each(|line|
> highlighter.parse_html_for_line_which_includes_newline(line));
> +
> + let mut output = String::from("<pre>\n");
> + for (n, line) in highlighter.finalize().lines().enumerate() {
> output.push_str(&format!(
> - "<a href='#L{0}' id='L{0}' class='line'>{0}</a>",
> - n + 1
> + "<a href='#L{0}' id='L{0}' class='line'>{0}</a>{1}\n",
> + n + 1, line
> ));
> - syntect::html::append_highlighted_html_for_styled_line(
> - &regions[..],
> - syntect::html::IncludeBackground::IfDifferent(bg),
> - &mut output,
> - );
> }
> output.push_str("</pre>\n");
>  
> diff --git a/templates/static/style.css b/templates/static/style.css
> index 77e6c4a..0399b3c 100644
> --- a/templates/static/style.css
> +++ b/templates/static/style.css
> @@ -5,7 +5,6 @@ body {
> margin: auto;
> font-family: "Archivo", Helvetica, Arial, Sans-serif;
> word-wrap: break-word;
> - background-color: white;
> line-height: 1.5;
> }
>  
> @@ -13,10 +12,6 @@ body {
> max-width: 80ch;
> }
>  
> -a:visited {
> - color: blue;
> -}
> -
> .repo-last-updated {
> font-style: italic;
> }
> @@ -53,10 +48,6 @@ a:visited {
> user-select: none; /* Non-prefixed version, currently */
> }
>  
> -:target {
> - background: #FFFACD;
> -}
> -
> .clone-url {
> font-family: "Roboto Mono", monospace;
> -webkit-user-select: all; /* Safari */
> @@ -66,31 +57,24 @@ a:visited {
> hr.thin {
> border: 0;
> height: 0;
> - /* top border for light color scheme */
> - border-top: 1px solid rgba(0, 0, 0, 0.1);
> - /* bottom border for dark color scheme */
> - border-bottom: 1px solid rgba(255, 255, 255, 0.3);
> + border-top: 1px solid;
> }
>  
> h1, h2, h3 {
> - margin: 0px;
> + margin: 0;
> }
>  
> pre {
> line-height: 1.2;
> font-size: 1rem;
> overflow: auto;
> - margin: 0px;
> - padding: 0px;
> -}
> -
> -tr:hover {
> - background-color: #f0f0f0;
> + margin: 0;
> + padding: 0;
> }
>  
> td {
> white-space: nowrap;
> - margin: 0px;
> + margin: 0;
> border-style:hidden;
> padding: 0;
> }
> @@ -114,17 +98,120 @@ table.core {
> }
> }
>  
> +/* for code highlighting */
> +
> +.markup.italic {
> + font-style: italic;
> +}
> +
> +.markup.bold {
> + font-weight: bold;
> +}
> +
> +/*
> +color scheme: Solarized
> +most of these classes are used by syntect
> +
> +Light is the default color scheme, not using a media query for browser
> support.
> +*/
> +/* TODO simplify, e.g. merge CSS classes. */
> +
> +.storage.modifier, .meta.scope.for-in-loop.shell,
> .variable.other.loop.shell, .meta.scope.case-block.shell,
> .meta.scope.case-body.shell {
> + color: #586e75; /* Solarized base01 */
> +}
> +
> +body, .code, .keyword.operator.comparison,
> .keyword.operator.assignment, .keyword.operator.arithmetic,
> .meta.brace.round, .meta.brace.curly, .punctuation.section,
> .punctuation.section.block, .punctuation.definition.parameters,
> .punctuation.section.group, .meta.selector.css, .text.html.basic,
> .meta.tag.other.html, .text.html.basic, .meta.tag.any.html,
> .text.html.basic, .meta.tag.block.any, .text.html.basic,
> .meta.tag.inline.any, .text.html.basic, .meta.tag.structure.any.html,
> .text.html.basic, .source.js.embedded.html,
> .punctuation.separator.key-value.html, .variable.other.readwrite.js,
> .variable.other.object.js, .variable.other.constant.js {
> + color: #657b83; /* Solarized base00 */
> +}
> +
> +hr.thin {
> + border-top-color: #657b83; /* Solarized base00 */
> +}
> +
> +.punctuation.definition.string {
> + color: #839496; /* Solarized base0 */
> +}
> +
> +.comment, .meta.documentation, .punctuation.definition.tag.html,
> .punctuation.definition.tag.begin, .punctuation.definition.tag.end,
> .meta.diff, .meta.diff.header,
> .constant.numeric.line-number.find-in-files {
> + color: #93a1a1; /* Solarized base1 */
> +}
> +
> +:target, tr:hover, .links, .meta.paragraph.markdown,
> .meta.dummy.line-break {
> + background-color: #eee8d5; /* Solarized base2 */
> +}
> +
> +body, .code {
> + background-color: #fdf6e3; /* Solarized base3 */
> +}
> +
> +.variable.function, .keyword.control.class, .entity.name,
> .entity.name.class, .entity.name.type.class,
> .entity.other.attribute-name, .entity.name.function, .constant,
> .constant.language, .meta.preprocessor, .support.constant.color,
> .invalid.deprecated.color.w3c-non-standard-color-name.scss,
> .entity.name.tag.css, .entity.name.tag.scss, .source.less,
> .keyword.control.html.elements, .source.sass, .keyword.control.untitled,
> .entity.other.attribute-name.class, .entity.other.attribute-name.id,
> .text.html.basic, .entity.other.attribute-name.html, .meta.tag.xml,
> .entity.other.attribute-name, .variable.other.constant.ruby,
> .meta.array, .support.function.construct.php, .meta.group.braces.tex,
> .string.other.math.tex, .string.other.math.tex,
> .support.type.exception.python, .markup.heading,
> .punctuation.definition.heading.markdown, .markup.changed.git_gutter {
> + color: #b58900; /* Solarized yellow */
> +}
> +
> +.meta.import, .keyword, .keyword.control.import,
> .keyword.control.import.from, .keyword.other.import,
> .keyword.control.at-rule.include, .keyword.control.at-rule.import,
> .entity.name.section, .constant.character, .constant.other,
> .support.type.exception, .keyword.other.special-method,
> .keyword.other.special-method.ruby,
> .entity.name.function.preprocessor.c, .meta.preprocessor.c.include,
> .meta.preprocessor.macro.c, .variable.parameter.function.latex,
> .support.function.section.latex {
> + color: #cb4b16; /* Solarized orange */
> +}
> +
> +.constant.character.escape, .support.function.construct,
> .keyword.other.new, .punctuation.separator.continuation,
> .other.package.exclude, .other.remove, .punctuation.section.group.tex,
> .punctuation.definition.arguments.begin.latex,
> .punctuation.definition.arguments.end.latex,
> .punctuation.definition.arguments.latex,
> .punctuation.definition.constant.math.tex,
> .punctuation.definition.string.begin.tex,
> .punctuation.definition.string.end.tex,
> .variable.parameter.definition.label.latex,
> .punctuation.definition.logical-expression.shell, .markup.deleted,
> .markup.deleted.git_gutter {
> + color: #dc322f; /* Solarized red */
> +}
> +
> +.variable.language {
> + color: #d33682; /* Solarized magenta */
> +}
> +
> +.constant.numeric, .constant.other.reference.link.markdown {
> + color: #6c71c4; /* Solarized violet */
> +}
> +
> +a, .variable, .entity.other.inherited-class, .entity.name.tag,
> .storage.type, .meta.brace.square, .punctuation.section.brackets,
> .entity.other.attribute-name.pseudo-element,
> .entity.other.attribute-name.tag.pseudo-element,
> .entity.other.attribute-name.pseudo-class,
> .entity.other.attribute-name.tag.pseudo-class, .support.function.perl,
> .meta.diff.range {
> + color: #268bd2; /* Solarized blue */
> +}
> +
> +.string, .string.regexp, .string.quoted.double, .string.quoted.single,
> .constant.other.symbol.ruby, .meta.preprocessor.c.include,
> .string.quoted.other.lt-gt.include.c, .meta.preprocessor.c.include,
> .punctuation.definition.string.begin.c, .meta.preprocessor.c.include,
> .punctuation.definition.string.end.c, .other.add, .text.tex.latex,
> .constant.other.math.tex, .constant.other.general.math.tex,
> .constant.other.general.math.tex, .constant.character.math.tex,
> .keyword.control.label.latex, .text.tex.latex,
> .constant.other.general.math.tex, .support.function.general.tex,
> .keyword.control.ref.latex, .markup.changed,
> .markup.underline.link.markdown, .meta.link.reference,
> .constant.other.reference.link.markdown,
> .entity.name.filename.find-in-files {
> + color: #2aa198; /* Solarized cyan */
> +}
> +
> +.keyword, .storage, .support, .support.type, .support.class,
> .punctuation.definition.variable, .support.function,
> .keyword.other.special-method.ruby, .support.function.be.latex,
> .storage.type.class.python, .storage.type.function.python,
> .storage.modifier.global.python, .storage.modifier.c++,
> .markup.inserted, .markup.quote, .markup.inserted.git_gutter {
> + color: #859900; /* Solarized green */
> +}
> +
> +.invalid {
> + background-color: #ec9489;
> +}
> +
> +/* Dark colour scheme for browsers that support it. */
> +
> @media (prefers-color-scheme: dark) {
> - body {
> - background: #212529;
> - color: #f8f9fa;
> + .storage.modifier, .meta.scope.for-in-loop.shell,
> .variable.other.loop.shell, .meta.scope.case-block.shell,
> .meta.scope.case-body.shell {
> + color: #93a1a1; /* Solarized base1 */
> + }
> +
> + body, .code, .keyword.operator.comparison,
> .keyword.operator.assignment, .keyword.operator.arithmetic,
> .meta.brace.round, .meta.brace.curly, .punctuation.section,
> .punctuation.section.block, .punctuation.definition.parameters,
> .punctuation.section.group, .meta.selector.css, .text.html.basic,
> .meta.tag.other.html, .text.html.basic, .meta.tag.any.html,
> .text.html.basic, .meta.tag.block.any, .text.html.basic,
> .meta.tag.inline.any, .text.html.basic, .meta.tag.structure.any.html,
> .text.html.basic, .source.js.embedded.html,
> .punctuation.separator.key-value.html, .variable.other.readwrite.js,
> .variable.other.object.js, .variable.other.constant.js {
> + color: #839496; /* Solarized base0 */
> + }
> +
> + hr.thin {
> + border-top-color: #839496; /* Solarized base0 */
> + }
> +
> + .punctuation.definition.string {
> + color: #657b83; /* Solarized base00 */
> + }
> +
> + .comment, .meta.documentation, .punctuation.definition.tag.html,
> .punctuation.definition.tag.begin, .punctuation.definition.tag.end,
> .meta.diff, .meta.diff.header,
> .constant.numeric.line-number.find-in-files {
> + color: #586e75; /* Solarized base01 */
> + }
> +
> + :target, tr:hover, .links, .meta.paragraph.markdown,
> .meta.dummy.line-break {
> + background-color: #073642; /* Solarized base02 */
> }
>  
> - :target {
> - background: #ffffff;
> + body, .code {
> + background-color: #002b36; /* Solarized base03 */
> }
>  
> - tr:hover {
> - background-color: #0f0f0f;
> + .invalid {
> + background-color: #6e2e32;
> }
> }
> --
> 2.20.1
Reply to thread Export thread (mbox)