From 2a9edba83a90889a5003203a7b63d9a239719a16 Mon Sep 17 00:00:00 2001 From: Jordan Doyle Date: Wed, 06 Jul 2022 22:43:53 +0100 Subject: [PATCH] Implement diff on commit view with syntax highlighting --- Cargo.lock | 402 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Cargo.toml | 2 ++ src/git.rs | 112 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/main.rs | 17 ++++++++++++++++- statics/style.css | 19 +++++++++++++++++++ src/methods/repo.rs | 6 ++++-- templates/repo/commit.html | 8 ++++++++ 7 files changed, 557 insertions(+), 9 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 1303f79..c750fb4 100644 --- a/Cargo.lock +++ a/Cargo.lock @@ -1,8 +1,32 @@ # This file is automatically @generated by Cargo. # It is not intended for manual editing. version = 3 [[package]] +name = "adler" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" + +[[package]] +name = "aho-corasick" +version = "0.7.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e37cfd5e7657ada45f742d6e99ca5788580b5c529dc78faf11ece6dc702656f" +dependencies = [ + "memchr", +] + +[[package]] +name = "ansi_colours" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32678233b67f9056b0c144b39d46dc3218637e8d84ad6038ded339e08b19620d" +dependencies = [ + "rgb", +] + +[[package]] name = "ansi_term" version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -173,13 +197,65 @@ "http", "http-body", "mime", +] + +[[package]] +name = "base64" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "904dfeac50f3cdaba28fc6f57fdcddb75f49ed61346676a78c4ffe55877802fd" + +[[package]] +name = "bat" +version = "0.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd1212d80800b3d7614b3725e0b2ee3b45b2b7484805d54b5660c8fa6f706305" +dependencies = [ + "ansi_colours", + "ansi_term", + "bincode", + "bytesize", + "clircle", + "console", + "content_inspector", + "encoding", + "flate2", + "globset", + "once_cell", + "path_abs", + "regex", + "semver", + "serde", + "serde_yaml", + "syntect", + "thiserror", + "unicode-width", + "walkdir", ] [[package]] +name = "bincode" +version = "1.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1f45e9417d87227c7a56d22e471c6206462cba514c7590c09aff4cf6d1ddcad" +dependencies = [ + "serde", +] + +[[package]] name = "bitflags" version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "bstr" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba3569f383e8f1598449f1a423e72e99569137b47740b1da11ef19af3d5c3223" +dependencies = [ + "memchr", +] [[package]] name = "bumpalo" @@ -192,6 +268,12 @@ version = "0.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2c676a478f63e9fa2dd5368a42f28bba0d6c560b775f38583c8bbaa7fcd67c9c" + +[[package]] +name = "bytemuck" +version = "1.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c53dfa917ec274df8ed3c572698f381a24eef2efba9492d797301b72b6db408a" [[package]] name = "bytes" @@ -200,6 +282,12 @@ checksum = "c4872d67bab6358e59559027aa3b9157c53d9358c51423c17554809a8858e0f8" [[package]] +name = "bytesize" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c58ec36aac5066d5ca17df51b3e70279f5670a72102f5752cb7e7c856adfc70" + +[[package]] name = "cache-padded" version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -293,6 +381,18 @@ checksum = "2850f2f5a82cbf437dd5af4d49848fbdfc27c157c3d010345776f952765261c5" dependencies = [ "os_str_bytes", +] + +[[package]] +name = "clircle" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e68bbd985a63de680ab4d1ad77b6306611a8f961b282c8b5ab513e6de934e396" +dependencies = [ + "cfg-if 1.0.0", + "libc", + "serde", + "winapi", ] [[package]] @@ -311,9 +411,42 @@ checksum = "30ed07550be01594c6026cff2a1d7fe9c8f683caa798e12b68694ac9e88286a3" dependencies = [ "cache-padded", +] + +[[package]] +name = "console" +version = "0.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a28b32d32ca44b70c3e4acd7db1babf555fa026e385fb95f18028f88848b3c31" +dependencies = [ + "encode_unicode", + "libc", + "once_cell", + "regex", + "terminal_size", + "unicode-width", + "winapi", +] + +[[package]] +name = "content_inspector" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7bda66e858c683005a53a9a60c69a4aca7eeaa45d124526e389f7aec8e62f38" +dependencies = [ + "memchr", ] [[package]] +name = "crc32fast" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b540bd8bc810d3885c6ea91e2018302f68baba2129ab3e88f32389ee9370880d" +dependencies = [ + "cfg-if 1.0.0", +] + +[[package]] name = "crossbeam-channel" version = "0.5.5" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -357,9 +490,79 @@ dependencies = [ "cfg-if 1.0.0", "once_cell", +] + +[[package]] +name = "encode_unicode" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a357d28ed41a50f9c765dbfe56cbc04a64e53e5fc58ba79fbc34c10ef3df831f" + +[[package]] +name = "encoding" +version = "0.2.33" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6b0d943856b990d12d3b55b359144ff341533e516d94098b1d3fc1ac666d36ec" +dependencies = [ + "encoding-index-japanese", + "encoding-index-korean", + "encoding-index-simpchinese", + "encoding-index-singlebyte", + "encoding-index-tradchinese", ] [[package]] +name = "encoding-index-japanese" +version = "1.20141219.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "04e8b2ff42e9a05335dbf8b5c6f7567e5591d0d916ccef4e0b1710d32a0d0c91" +dependencies = [ + "encoding_index_tests", +] + +[[package]] +name = "encoding-index-korean" +version = "1.20141219.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4dc33fb8e6bcba213fe2f14275f0963fd16f0a02c878e3095ecfdf5bee529d81" +dependencies = [ + "encoding_index_tests", +] + +[[package]] +name = "encoding-index-simpchinese" +version = "1.20141219.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d87a7194909b9118fc707194baa434a4e3b0fb6a5a757c73c3adb07aa25031f7" +dependencies = [ + "encoding_index_tests", +] + +[[package]] +name = "encoding-index-singlebyte" +version = "1.20141219.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3351d5acffb224af9ca265f435b859c7c01537c0849754d3db3fdf2bfe2ae84a" +dependencies = [ + "encoding_index_tests", +] + +[[package]] +name = "encoding-index-tradchinese" +version = "1.20141219.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd0e20d5688ce3cab59eb3ef3a2083a5c77bf496cb798dc6fcdb75f323890c18" +dependencies = [ + "encoding_index_tests", +] + +[[package]] +name = "encoding_index_tests" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a246d82be1c9d791c5dfde9a2bd045fc3cbba3fa2b11ad558f27d01712f00569" + +[[package]] name = "error-chain" version = "0.12.4" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -381,6 +584,16 @@ checksum = "c3fcf0cee53519c866c09b5de1f6c56ff9d647101f81c1964fa632e148896cdf" dependencies = [ "instant", +] + +[[package]] +name = "flate2" +version = "1.0.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f82b0f4c27ad9f8bfd1f3208d882da2b09c301bc1c828fd3a00d0216d2fbbff6" +dependencies = [ + "crc32fast", + "miniz_oxide", ] [[package]] @@ -540,6 +753,19 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9b919933a397b79c37e33b77bb2aa3dc8eb6e165ad809e58ff75bc7db2e34574" + +[[package]] +name = "globset" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0a1e17342619edbc21a964c2afbeb6c820c6a2560032872f397bb97ea127bd0a" +dependencies = [ + "aho-corasick", + "bstr", + "fnv", + "log", + "regex", +] [[package]] name = "hashbrown" @@ -751,7 +977,22 @@ "libc", "pkg-config", "vcpkg", +] + +[[package]] +name = "line-wrap" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f30344350a2a51da54c1d53be93fade8a237e545dbcc4bdbe635413f2117cab9" +dependencies = [ + "safemem", ] + +[[package]] +name = "linked-hash-map" +version = "0.5.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0717cef1bc8b636c6e1c1bbdefc09e6322da8a9321966e8928ef80d20f7f770f" [[package]] name = "lock_api" @@ -841,6 +1082,15 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" + +[[package]] +name = "miniz_oxide" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6f5c75688da582b8ffc1f1799e9db273f32133c49e048f614d22ec3256773ccc" +dependencies = [ + "adler", +] [[package]] name = "mio" @@ -932,6 +1182,28 @@ version = "1.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "18a6dbe30758c9f83eb00cbea4ac95966305f5a7772f3f42ebfc7fc7eddbd8e1" + +[[package]] +name = "onig" +version = "6.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1eb3502504c9c8b06634b38bfdda86a9a8cef6277f3dec4d8b17c115110dd2a3" +dependencies = [ + "bitflags", + "lazy_static", + "libc", + "onig_sys", +] + +[[package]] +name = "onig_sys" +version = "69.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8bf3fbc9b931b6c9af85d219c7943c274a6ad26cff7488a2210215edd5f49bf8" +dependencies = [ + "cc", + "pkg-config", +] [[package]] name = "openssl-probe" @@ -992,6 +1264,15 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ecba01bf2678719532c5e3059e0b5f0811273d94b397088b82e3bd0a78c78fdd" + +[[package]] +name = "path_abs" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05ef02f6342ac01d8a93b65f96db53fe68a92a15f41144f97fb00a9e669633c3" +dependencies = [ + "std_prelude", +] [[package]] name = "percent-encoding" @@ -1074,6 +1355,20 @@ version = "0.3.25" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1df8c4ec4b0627e53bdf214615ad287367e482558cf84b109250b37464dc03ae" + +[[package]] +name = "plist" +version = "1.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd39bc6cdc9355ad1dc5eeedefee696bb35c34caf21768741e81826c0bbd7225" +dependencies = [ + "base64", + "indexmap", + "line-wrap", + "serde", + "time 0.3.11", + "xml-rs", +] [[package]] name = "polling" @@ -1264,7 +1559,24 @@ checksum = "62f25bc4c7e55e0b0b7a1d43fb893f4fa1361d0abe38b9ce4f323c2adfe6ef42" dependencies = [ "bitflags", +] + +[[package]] +name = "regex" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c4eb3267174b8c6c2f654116623910a0fef09c4753f8dd83db29c48a0df988b" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", ] + +[[package]] +name = "regex-syntax" +version = "0.6.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a3f87b73ce11b1619a3c6332f45341e0047173771e8b8b73f87bfeefb7b56244" [[package]] name = "remove_dir_all" @@ -1273,6 +1585,15 @@ checksum = "3acd125665422973a33ac9d3dd2df85edad0f4ae9b00dafb1a05e43a9f5ef8e7" dependencies = [ "winapi", +] + +[[package]] +name = "rgb" +version = "0.8.33" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3b221de559e4a29df3b957eec92bc0de6bc8eaf6ca9cfed43e5e1d67ff65a34" +dependencies = [ + "bytemuck", ] [[package]] @@ -1282,6 +1603,7 @@ "arc-swap", "askama", "axum", + "bat", "clap", "futures", "git2", @@ -1291,6 +1613,7 @@ "moka", "path-clean", "serde", + "syntect", "time 0.3.11", "timeago", "tokio", @@ -1307,6 +1630,12 @@ version = "1.0.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f3f6f92acf49d1b98f7a81226834412ada05458b7364277387724a237f062695" + +[[package]] +name = "safemem" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef703b7cb59335eae2eb93ceb664c0eb7ea6bf567079d843e09420219668e072" [[package]] name = "same-file" @@ -1380,8 +1709,20 @@ dependencies = [ "form_urlencoded", "itoa", + "ryu", + "serde", +] + +[[package]] +name = "serde_yaml" +version = "0.8.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "707d15895415db6628332b737c838b88c598522e4dc70647e59b72312924aebc" +dependencies = [ + "indexmap", "ryu", "serde", + "yaml-rust", ] [[package]] @@ -1444,6 +1785,12 @@ "libc", "winapi", ] + +[[package]] +name = "std_prelude" +version = "0.2.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8207e78455ffdf55661170876f88daf85356e4edd54e0a3dbc79586ca1e50cbe" [[package]] name = "strsim" @@ -1467,6 +1814,29 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "20518fe4a4c9acf048008599e464deb21beeae3d3578418951a189c235a7a9a8" + +[[package]] +name = "syntect" +version = "5.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c6c454c27d9d7d9a84c7803aaa3c50cd088d2906fe3c6e42da3209aa623576a8" +dependencies = [ + "bincode", + "bitflags", + "flate2", + "fnv", + "lazy_static", + "once_cell", + "onig", + "plist", + "regex-syntax", + "serde", + "serde_derive", + "serde_json", + "thiserror", + "walkdir", + "yaml-rust", +] [[package]] name = "tagptr" @@ -1495,6 +1865,16 @@ checksum = "bab24d30b911b2376f3a13cc2cd443142f0c81dda04c118693e35b3835757755" dependencies = [ "winapi-util", +] + +[[package]] +name = "terminal_size" +version = "0.1.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "633c1a546cee861a1a6d0dc69ebeca693bf4296661ba7852b9d21d159e0506df" +dependencies = [ + "libc", + "winapi", ] [[package]] @@ -1548,6 +1928,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "72c91f41dcb2f096c05f0873d667dceec1087ce5bcf984ec8ffb19acddbb3217" dependencies = [ + "itoa", "libc", "num_threads", ] @@ -1764,6 +2145,12 @@ dependencies = [ "tinyvec", ] + +[[package]] +name = "unicode-width" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3ed742d4ea2bd1176e236172c8429aaf54486e7ac098db29ffe6529e0ce50973" [[package]] name = "url" @@ -1989,3 +2376,18 @@ version = "0.36.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c811ca4a8c853ef420abd8592ba53ddbbac90410fab6903b3e79972a631f7680" + +[[package]] +name = "xml-rs" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2d7d3948613f75c98fd9328cfdcc45acc4d360655289d0a7d4ec931392200a3" + +[[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 index acd49b9..2577af8 100644 --- a/Cargo.toml +++ a/Cargo.toml @@ -9,6 +9,7 @@ askama = "0.11" arc-swap = "1.5" axum = "0.5" +bat = { version = "0.21", default-features = false, features = ["build-assets"] } clap = { version = "3.2", features = ["cargo"] } futures = "0.3" git2 = "0.14" @@ -18,6 +19,7 @@ moka = { version = "0.9", features = ["future"] } path-clean = "0.1" serde = { version = "1.0", features = ["derive"] } +syntect = "5" time = "0.3" timeago = "0.3" tokio = { version = "1.19", features = ["full"] } diff --git a/src/git.rs b/src/git.rs index 74fcf82..27a6315 100644 --- a/src/git.rs +++ a/src/git.rs @@ -7,8 +7,12 @@ }; use arc_swap::ArcSwapOption; -use git2::{ObjectType, Oid, Repository, Signature}; +use git2::{ + DiffFormat, DiffLineType, DiffOptions, DiffStatsFormat, ObjectType, Oid, Repository, Signature, +}; use moka::future::Cache; +use syntect::html::{ClassStyle, ClassedHTMLGenerator}; +use syntect::parsing::SyntaxSet; use time::OffsetDateTime; pub type RepositoryMetadataList = BTreeMap, Vec>; @@ -42,7 +46,12 @@ } impl Git { - pub async fn get_commit<'a>(&'a self, repo: PathBuf, commit: &str) -> Arc { + pub async fn get_commit<'a>( + &'a self, + repo: PathBuf, + commit: &str, + syntax_set: Arc, + ) -> Arc { let commit = Oid::from_str(commit).unwrap(); self.commits @@ -50,8 +59,14 @@ tokio::task::spawn_blocking(move || { let repo = Repository::open_bare(repo).unwrap(); let commit = repo.find_commit(commit).unwrap(); + let (diff_output, diff_stats) = + fetch_diff_and_stats(&repo, &commit, &syntax_set); + + let mut commit = Commit::from(commit); + commit.diff_stats = diff_stats; + commit.diff = diff_output; - Arc::new(Commit::from(commit)) + Arc::new(commit) }) .await .unwrap() @@ -153,13 +168,17 @@ .await } - pub async fn get_latest_commit(&self, repo: PathBuf) -> Commit { + pub async fn get_latest_commit(&self, repo: PathBuf, syntax_set: Arc) -> Commit { tokio::task::spawn_blocking(move || { let repo = Repository::open_bare(repo).unwrap(); let head = repo.head().unwrap(); let commit = head.peel_to_commit().unwrap(); - - Commit::from(commit) + let (diff_output, diff_stats) = fetch_diff_and_stats(&repo, &commit, &syntax_set); + + let mut commit = Commit::from(commit); + commit.diff_stats = diff_stats; + commit.diff = diff_output; + commit }) .await .unwrap() @@ -316,6 +335,8 @@ parents: Vec, summary: String, body: String, + pub diff_stats: String, + pub diff: String, } impl From> for Commit { @@ -328,6 +349,8 @@ parents: commit.parent_ids().map(|v| v.to_string()).collect(), summary: commit.summary().unwrap().to_string(), body: commit.body().map(ToString::to_string).unwrap_or_default(), + diff_stats: String::with_capacity(0), + diff: String::with_capacity(0), } } } @@ -360,6 +383,83 @@ pub fn body(&self) -> &str { &self.body } +} + +fn fetch_diff_and_stats( + repo: &git2::Repository, + commit: &git2::Commit<'_>, + syntax_set: &SyntaxSet, +) -> (String, String) { + let current_tree = commit.tree().unwrap(); + let parent_tree = commit.parents().next().and_then(|v| v.tree().ok()); + let mut diff_opts = DiffOptions::new(); + let diff = repo + .diff_tree_to_tree( + parent_tree.as_ref(), + Some(¤t_tree), + Some(&mut diff_opts), + ) + .unwrap(); + let diff_stats = diff + .stats() + .unwrap() + .to_buf(DiffStatsFormat::FULL, 80) + .unwrap() + .as_str() + .unwrap() + .to_string(); + let diff_output = format_diff(&diff, &syntax_set); + + (diff_output, diff_stats) +} + +fn format_diff(diff: &git2::Diff<'_>, syntax_set: &SyntaxSet) -> String { + let mut diff_output = String::new(); + + diff.print(DiffFormat::Patch, |delta, _diff_hunk, diff_line| { + let (class, prefix, should_highlight_as_source) = match diff_line.origin_value() { + DiffLineType::Addition => (Some("add-line"), "+", true), + DiffLineType::Deletion => (Some("remove-line"), "-", true), + DiffLineType::Context => (None, " ", true), + DiffLineType::AddEOFNL => (Some("remove-line"), "", false), + DiffLineType::DeleteEOFNL => (Some("add-line"), "", false), + DiffLineType::FileHeader => (Some("file-header"), "", false), + _ => (None, "", false), + }; + + let line = std::str::from_utf8(diff_line.content()).unwrap(); + + let extension = if should_highlight_as_source { + let path = delta.new_file().path().unwrap(); + path.extension() + .or(path.file_name()) + .unwrap() + .to_string_lossy() + } else { + Cow::Borrowed("patch") + }; + let syntax = syntax_set + .find_syntax_by_extension(&extension) + .unwrap_or(syntax_set.find_syntax_plain_text()); + let mut html_generator = + ClassedHTMLGenerator::new_with_class_style(syntax, &syntax_set, ClassStyle::Spaced); + html_generator + .parse_html_for_line_which_includes_newline(line) + .unwrap(); + if let Some(class) = class { + diff_output.push_str(&format!("")); + } + diff_output.push_str(prefix); + diff_output.push_str(&html_generator.finalize()); + if class.is_some() { + diff_output.push_str(""); + } + + true + }) + .unwrap(); + + diff_output } fn fetch_repository_metadata_impl( diff --git a/src/main.rs b/src/main.rs index 5fc349e..a6a4711 100644 --- a/src/main.rs +++ a/src/main.rs @@ -1,9 +1,12 @@ #![deny(clippy::pedantic)] use axum::{ body::Body, handler::Handler, http::HeaderValue, response::Response, routing::get, Extension, Router, }; +use bat::assets::HighlightingAssets; +use std::sync::Arc; +use syntect::html::ClassStyle; use tower_layer::layer_fn; use crate::{git::Git, layers::logger::LoggingMiddleware}; @@ -21,15 +24,27 @@ let subscriber = subscriber.pretty(); subscriber.init(); + let bat_assets = HighlightingAssets::from_binary(); + let syntax_set = bat_assets.get_syntax_set().unwrap().clone(); + let theme = bat_assets.get_theme("GitHub"); + let css = Box::leak( + syntect::html::css_for_theme_with_class_style(theme, ClassStyle::Spaced) + .unwrap() + .into_boxed_str() + .into_boxed_bytes(), + ); + let app = Router::new() .route("/", get(methods::index::handle)) .route( "/style.css", get(static_css(include_bytes!("../statics/style.css"))), ) + .route("/highlight.css", get(static_css(css))) .fallback(methods::repo::service.into_service()) .layer(layer_fn(LoggingMiddleware)) - .layer(Extension(Git::default())); + .layer(Extension(Git::default())) + .layer(Extension(Arc::new(syntax_set))); axum::Server::bind(&"127.0.0.1:3333".parse().unwrap()) .serve(app.into_make_service_with_connect_info::()) diff --git a/statics/style.css b/statics/style.css index 51cae2b..4cf9841 100644 --- a/statics/style.css +++ a/statics/style.css @@ -91,3 +91,22 @@ font-size: 1.5em; font-weight: bold; } + + +.diff-file-header { + font-weight: bold; +} + +.diff-file-header > span > span { + font-weight: normal; +} + +.diff-add-line { + background: #e6ffec; + display: block; +} + +.diff-remove-line { + background: #ffebe9; + display: block; +} diff --git a/src/methods/repo.rs b/src/methods/repo.rs index a1d583c..a04fd75 100644 --- a/src/methods/repo.rs +++ a/src/methods/repo.rs @@ -14,6 +14,7 @@ }; use path_clean::PathClean; use serde::Deserialize; +use syntect::parsing::SyntaxSet; use tower::{util::BoxCloneService, Service}; use super::filters; @@ -205,6 +206,7 @@ Extension(repo): Extension, Extension(RepositoryPath(repository_path)): Extension, Extension(git): Extension, + Extension(syntax_set): Extension>, Query(query): Query, ) -> Html { #[derive(Template)] @@ -218,9 +220,9 @@ View { repo, commit: if let Some(commit) = query.id { - git.get_commit(repository_path, &commit).await + git.get_commit(repository_path, &commit, syntax_set).await } else { - Arc::new(git.get_latest_commit(repository_path).await) + Arc::new(git.get_latest_commit(repository_path, syntax_set).await) }, } .render() diff --git a/templates/repo/commit.html b/templates/repo/commit.html index 70eeee2..647a32e 100644 --- a/templates/repo/commit.html +++ a/templates/repo/commit.html @@ -1,5 +1,9 @@ {% extends "repo/base.html" %} +{% block head %} + +{% endblock %} + {% block commit_nav_class %}active{% endblock %} {% block content %} @@ -34,4 +38,8 @@

{{ commit.summary() }}

{{ commit.body() }}
+ +

Diff

+
{{ commit.diff_stats|safe }}
+{{ commit.diff|safe }}
{% endblock %}-- rgit 0.1.3