mygit

[UNMAINTAINED] A cgit/webgit alternative, written in Rust
Log | Files | Refs | README | LICENSE

commit 54d2075fc85826a4b6601f239965156aa4f9333f
parent e88eea30a74b94f48c9d60cb1f6cbaa4e082896d
Author: alex wennerberg <alex@alexwennerberg.com>
Date:   Wed, 17 Mar 2021 17:04:14 -0700

Add file rendering, syntax highlighting

Some other minor changes in this too

Diffstat:
MCargo.lock | 55+++++++++++++++++++++++++++++++++++++++++++++++++++++++
MCargo.toml | 2+-
MREADME.md | 2+-
Msrc/main.rs | 60++++++++++++++++++++++++++++++++++++++++++++++++++++++++----
Mtemplates/file.html | 5+++--
Mtemplates/index.html | 3+--
Mtemplates/static/style.css | 10++++++++++
Mtemplates/tree.html | 7+++++--
8 files changed, 132 insertions(+), 12 deletions(-)

diff --git a/Cargo.lock b/Cargo.lock @@ -1,6 +1,12 @@ # This file is automatically @generated by Cargo. # It is not intended for manual editing. [[package]] +name = "adler" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" + +[[package]] name = "aead" version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -561,6 +567,15 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dcb25d077389e53838a8158c8e99174c5a9d902dee4904320db714f3c653ffba" [[package]] +name = "crc32fast" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81156fece84ab6a9f2afdb109ce3ae577e42b1228441eded99bd77f627953b1a" +dependencies = [ + "cfg-if 1.0.0", +] + +[[package]] name = "crossbeam-queue" version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -695,6 +710,18 @@ dependencies = [ ] [[package]] +name = "flate2" +version = "1.0.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd3aec53de10fe96d7d8c565eb17f2c687bb5518a2ec453b5b1252964526abe0" +dependencies = [ + "cfg-if 1.0.0", + "crc32fast", + "libc", + "miniz_oxide", +] + +[[package]] name = "fnv" version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -1089,6 +1116,12 @@ dependencies = [ ] [[package]] +name = "linked-hash-map" +version = "0.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7fb9b38af92608140b86b693604b9ffcc5824240a484d1ecd4795bacb2fe88f3" + +[[package]] name = "log" version = "0.4.14" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -1117,6 +1150,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0ee1c47aaa256ecabcaea351eae4a9b01ef39ed810004e298d2511ed284b1525" [[package]] +name = "miniz_oxide" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a92518e98c078586bc6c934028adcca4c92a53d6a958196de835170a01d84e4b" +dependencies = [ + "adler", + "autocfg", +] + +[[package]] name = "mygit" version = "0.1.0" dependencies = [ @@ -1732,7 +1775,9 @@ version = "4.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2bfac2b23b4d049dc9a89353b4e06bbc85a8f42020cccbe5409a115cf19031e5" dependencies = [ + "bincode", "bitflags", + "flate2", "fnv", "lazy_static", "lazycell", @@ -1743,6 +1788,7 @@ dependencies = [ "serde_derive", "serde_json", "walkdir", + "yaml-rust", ] [[package]] @@ -2121,3 +2167,12 @@ name = "xml-rs" version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b07db065a5cf61a7e4ba64f29e67db906fb1787316516c4e6e5ff0fea1efcd8a" + +[[package]] +name = "yaml-rust" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56c1936c4cc7a1c9ab21a1ebb602eb942ba868cbd44a99cb7cdc5892335e1c85" +dependencies = [ + "linked-hash-map", +] diff --git a/Cargo.toml b/Cargo.toml @@ -19,6 +19,6 @@ percent-encoding = "2.1" pico-args = "0.4" pulldown-cmark = "0.8" serde = { version = "1.0", features = ["derive"] } -syntect = {version="4.5", default-features = false, features=["html", "regex-onig"]} +syntect = "4.5" tide = "0.16" toml = "0.5.8" diff --git a/README.md b/README.md @@ -14,7 +14,7 @@ I am working on a sibling project to this that handles mailing list archives: https://git.sr.ht/~aw/rusty-inbox This project is on sr.ht until I can get it self-hosted. -* [ticket tracker](https://todo.sr.ht/~aw/grifter) +* [ticket tracker](https://todo.sr.ht/~aw/mygit) * [patches](https://lists.sr.ht/~aw/patches) ## Design diff --git a/src/main.rs b/src/main.rs @@ -1,12 +1,16 @@ use anyhow::Result; use askama::Template; -use git2::{Commit, Diff, DiffDelta, DiffFormat, Oid, Reference, Repository, Tree, TreeEntry}; +use git2::{ + Commit, Diff, DiffDelta, DiffFormat, Object, Oid, Reference, Repository, Tree, TreeEntry, +}; use once_cell::sync::Lazy; use pico_args; use serde::{Deserialize, Serialize}; use std::fs; use std::path::Path; use std::str; +use syntect::highlighting::{Color, ThemeSet}; +use syntect::parsing::{SyntaxReference, SyntaxSet}; use tide::prelude::*; use tide::Request; @@ -36,7 +40,7 @@ mod defaults { } pub fn site_name() -> String { - "grifter".to_string() + "mygit".to_string() } pub fn export_ok() -> String { @@ -333,11 +337,58 @@ async fn repo_commit(req: Request<()>) -> tide::Result { #[template(path = "file.html")] // using the template in this path, relative struct RepoFileTemplate<'a> { repo: &'a Repository, + tree_entry: &'a TreeEntry<'a>, + file_text: &'a str, } async fn repo_file(req: Request<()>) -> tide::Result { + // TODO renmae for clarity let repo = repo_from_request(req.param("repo_name")?)?; - let tmpl = RepoFileTemplate { repo: &repo }; + // If directory -- show tree TODO + let head = repo.head()?; + let spec = req.param("ref").unwrap_or(head.shorthand().unwrap()); + let commit = repo.revparse_single(spec)?.peel_to_commit()?; + let tree = commit.tree()?; + let tree_entry = tree.get_name(req.param("object_name")?).unwrap(); + // 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 extension = std::path::Path::new(tree_entry.name().unwrap()) + .extension() + .and_then(std::ffi::OsStr::to_str) + .unwrap_or(""); + let syntax_reference = syntax_set + .find_syntax_by_extension(extension) + .unwrap_or(syntax_set.find_syntax_plain_text()); + let ts = ThemeSet::load_defaults(); + let theme = &ts.themes["InspiredGitHub"]; // TODO make customizable + let tree_obj = tree_entry.to_object(&repo)?; + if tree_obj.as_tree().is_some() { + // TODO render tree + } + 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); + output.push_str(&format!( + "<a href='#L{0}' id='L{0}' class='line'>{0:>6}</a>&nbsp;", + n + )); + syntect::html::append_highlighted_html_for_styled_line( + &regions[..], + syntect::html::IncludeBackground::IfDifferent(bg), + &mut output, + ); + } + output.push_str("</pre>\n"); + + let tmpl = RepoFileTemplate { + repo: &repo, + tree_entry: &tree_entry, + file_text: &output, + }; Ok(tmpl.into()) } @@ -408,7 +459,8 @@ async fn main() -> Result<(), std::io::Error> { app.at("/:repo_name/log/:ref").get(repo_log); // ref optional app.at("/:repo_name/tree").get(repo_tree); app.at("/:repo_name/tree/:ref").get(repo_tree); - app.at("/:repo_name/tree/:ref/item/:file").get(repo_file); + app.at("/:repo_name/tree/:ref/item/:object_name") + .get(repo_file); // Raw files, patch files app.listen("127.0.0.1:8081").await?; Ok(()) diff --git a/templates/file.html b/templates/file.html @@ -2,6 +2,7 @@ {% block content %} {% include "repo-navbar.html" %} - <h2>abranch</h2> - <pre class="code"> file text goes here</pre> + branch + <h2>{{tree_entry.name().unwrap()}}</h2> + {{file_text|safe}} {% endblock %} diff --git a/templates/index.html b/templates/index.html @@ -2,8 +2,7 @@ {% block content %} <div class="page-title"><h1>Repositories</h1></div> - <h2>Subheading</h2> - <div style=> + <div> <table> {% for repo in repos %} <tr> diff --git a/templates/static/style.css b/templates/static/style.css @@ -26,6 +26,16 @@ body { font-size: .9rem; } +.line { + text-decoration: none; + -webkit-touch-callout: none; /* iOS Safari */ + -webkit-user-select: none; /* Safari */ + -khtml-user-select: none; /* Konqueror HTML */ + -moz-user-select: none; /* Old versions of Firefox */ + -ms-user-select: none; /* Internet Explorer/Edge */ + user-select: none; /* Non-prefixed version, currently +} + .clone-url { font-family: "Roboto Mono", monospace; -webkit-user-select: all; /* Safari */ diff --git a/templates/tree.html b/templates/tree.html @@ -11,10 +11,13 @@ </td> <td> <a href="/{{repo|repo_name|urlencode_strict}}/tree/{{ spec }}/item/{{entry.name().unwrap()}}"> - {{ entry.name().unwrap() }}</a> + {{ entry.name().unwrap() }}{% if entry.to_object(repo).unwrap().as_tree().is_some() %}/{% endif %}</a> </td> <td> - filesize here + {% let obj = entry.to_object(repo).unwrap() %} + {% if obj.as_blob().is_some() %} + {{ entry.to_object(repo).unwrap().into_blob().unwrap().content().len()|filesizeformat }} + {% endif %} </td> </tr> {% endfor %}