~/selfhut

1268e21eab953f2cf088dcdc958b11f20e2d8ea1 — Arthur Melton 9d1583ba 2 years ago
blame working
A src/git/blame.rs +30 -0
@@ 0,0 1,30 @@

use crate::config::CONFIG;
use std::path::Path;
use crate::git::commits::{Commits, get_commits};
use serde_derive::Serialize;
use std::ops::Range;

pub fn blame<'a>(repo: String, branch: String, path: String) -> Option<Vec<Blame>> {
    if branch.contains(":") {
        return None;
    }
    let mut repo_path = CONFIG.git_location.clone();
    repo_path.push(format!("{}.git", repo));
    let repo_clone = repo.clone();
    let repo = git2::Repository::open(repo_path).ok()?;
    let blame = repo.blame_file(Path::new(&path), None).ok()?;
    let mut blames = Vec::new();
    for i in blame.iter() {
        blames.push( Blame {
            commit: (*get_commits(repo_clone.clone(), 1, Some(i.final_commit_id().to_string()), None)?.first()?).clone(),
            lines: (0..i.lines_in_hunk()-1).collect()
        });
    }
    Some(blames)
}

#[derive(Serialize, Clone)]
pub struct Blame {
    commit: Commits,
    lines: Vec<usize>
}

M src/git/file.rs => src/git/file.rs +0 -18
@@ 9,15 9,6 @@ pub fn files(repo: String, branch: String, path: String) -> Option<Vec<File>> {

    if branch.contains(":") {
        return None;
    }
    let mut location = CONFIG.git_location.clone();
    location.push(path.clone());
    if location
        .components()
        .into_iter()
        .any(|x| x == Component::ParentDir)
    {
        return None;
    }
    let mut repo_path = CONFIG.git_location.clone();
    repo_path.push(format!("{}.git", repo));
    let repo = git2::Repository::open(repo_path).ok()?;

@@ 56,15 47,6 @@ pub fn file(repo: String, branch: String, path: String) -> Option<(File, Option<

    if branch.contains(":") {
        return None;
    }
    let mut location = CONFIG.git_location.clone();
    location.push(path.clone());
    if location
        .components()
        .into_iter()
        .any(|x| x == Component::ParentDir)
    {
        return None;
    }
    let mut repo_path = CONFIG.git_location.clone();
    repo_path.push(format!("{}.git", repo));
    let repo = git2::Repository::open(repo_path).ok()?;

M src/git/mod.rs => src/git/mod.rs +1 -0
@@ 4,3 4,4 @@ pub mod file;

pub mod main_branch;
pub mod repos;
pub mod tag;
pub mod blame;

M src/main.rs => src/main.rs +2 -0
@@ 15,6 15,7 @@ use crate::repository::log;

use crate::repository::raw;
use crate::repository::summary;
use crate::repository::tree;
use crate::repository::blame;
use crate::utils::own_pathbuf::PathBufWithDotfiles;
use rocket_dyn_templates::Template;


@@ 34,6 35,7 @@ fn rocket() -> _ {

                raw::raw,
                log::log_main,
                log::log,
                blame::blames,
            ],
        )
        .attach(Template::fairing())

A src/repository/blame.rs +69 -0
@@ 0,0 1,69 @@

use crate::config::CONFIG;
use crate::git::file::files;
use rocket_dyn_templates::{context, Template};
use crate::git::commits::get_commits;
use crate::utils::repo_config::repo_config;
use syntect::html::highlighted_html_for_string;
use std::path::Path;
use syntect::highlighting::ThemeSet;
use syntect::parsing::SyntaxSet;
use crate::git::file::file;
use crate::git::blame::blame;
use crate::PathBufWithDotfiles;
use std::ffi::OsStr;

#[get("/<repo>/blame/<branch>/<location..>", rank = 2)]
pub fn blames(repo: String, branch: String, location: PathBufWithDotfiles) -> Option<Template> {
    let blames = blame(repo.clone(), branch.clone(), location.get().display().to_string());
    let file = file(
        repo.clone(),
        branch.clone(),
        location.get().display().to_string(),
    )?;
    let mut content = "".to_string();
    let mut lines: Vec<usize> = (1..1).collect();
    if file.1.is_some() {
        let ps = SyntaxSet::load_defaults_newlines();
        let ts = ThemeSet::load_defaults();
        let syntax = match ps.find_syntax_by_extension(
            Path::new(&file.0.name)
                .extension()
                .and_then(OsStr::to_str)
                .unwrap_or("txt"),
        ) {
            Some(x) => x,
            None => ps.find_syntax_by_extension("txt").unwrap(),
        };
        let s = file.1.as_ref().unwrap();
        lines = (1..s.lines().count() + 1).collect();
        content =
            highlighted_html_for_string(s, &ps, syntax, &ts.themes["Solarized (light)"])
                .ok()?
    }
    Some(Template::render(
        "repository/blame",
        context! {
            title: format!("/{} :: {}", location.get().display(), repo.clone()),
            repo: repo.clone(),
            config: repo_config(repo.clone()),
            domain: CONFIG.domain.to_string(),
            active: "tree",
            commit: match get_commits(repo.clone(), 1, None, Some(format!("{}", location.get().display()).replace("//", "/"))) {
                Some(x) => match x.clone().get(0) {
                    Some(x) => Some(x.clone()),
                    None => None
                }
                None => None
            },
            files: file,
            content,
            lines,
            fluid: "true",
            blame: blames,
            branch: branch.clone(),
            current_dir_file: format!("/{}/", location.get().display()).replace("//", "/"),
            current_dir: format!("/{}", location.get().display()),
            payment: CONFIG.payment_link.clone()
        },
    ))
}

M src/repository/mod.rs => src/repository/mod.rs +1 -0
@@ 2,3 2,4 @@ pub mod log;

pub mod raw;
pub mod summary;
pub mod tree;
pub mod blame;

A templates/repository/blame.html.hbs +93 -0
@@ 0,0 1,93 @@

{{#*inline "page"}}
{{> navbar}}
<div class="header-extension" style="margin-bottom: 0;">
	<div class="blob container-fluid">
		<span>
			{{repo}}{{current_dir}}
			<span class="text-muted" style="margin-left: 1rem">
			<span title="{{files.0.properties_int}}">
			{{files.0.properties}}
			</span>
			<span title="{{files.0.size}}">
			{{files.0.size}}
			</span>
			</span>
			<div class="blob-nav" style="margin-left: 1rem">
				<ul class="nav nav-tabs">
					<li class="nav-item">
						<a class="nav-link" href="/{{repo}}/tree/{{branch}}{{current_dir}}">View</a>
					</li>
					<li class="nav-item">
						<a class="nav-link" href="/{{repo}}/log/{{branch}}{{current_dir}}">Log</a>
					</li>
					<li class="nav-item">
						<a class="nav-link active" href="/{{repo}}/blame/{{branch}}{{current_dir}}">Blame</a>
					</li>
					<li class="nav-item">
						<a class="nav-link" href="/{{repo}}/blob/{{branch}}{{current_dir}}">View Raw</a>
					</li>
				</ul>
			</div>
		</span>
		{{#if commit}}
		<div class="commit">
			<a href="/{{repo}}/commit/{{commit.commit_hash}}">{{commit.commit_hash_short}}</a> —
			{{commit.commitie}}
			{{commit.commit}}
			<span class="text-muted">
			<span title="{{commit.time_utc}}">{{commit.time_relitive}}</span>
			</span>
		</div>
        <div class="clearfix">
        </div>
		{{/if}}
	</div>
</div>
<div class="container-fluid code-viewport">
	<div class="row" style="margin-right: 0;">
		<div class="col-md-12 code-view">
            <pre class="ruler"><span>
            </span></pre>
            <pre class="blame-user">
{{#each blame}}
<div class="hunk"><a href="/{{../repo}}/commit/{{this.commit.commit_hash}}">{{this.commit.commit_hash_short}}</a> {{this.commit.commitie}}</div>{{#each this.lines}}
{{/each}}
{{/each}}
</pre>
            <pre class="blame-time">
{{#each blame}}
<div class="hunk"><a href="/{{../repo}}/blame/{{this.commit.commit_hash}}{{../current_dir}}"><span title="{{this.commit.time_utc}}">{{this.commit.time_relitive}}</span></a></div>{{#each this.lines}}
{{/each}}
{{/each}}
</pre>
            <pre class="lines">
{{#each lines}}
<a href="#L{{this}}" id="L{{this}}">{{this}}</a>
{{/each}}
</pre>
			<div class="highlight">
				{{#if files.1}}
				{{{content}}}
				{{else}}
				<div style="padding: 1rem">
					<p> 
						{{files.0.size}} Binary file not shown.
					</p>
					<p>
						<a class="btn btn-primary" href="/{{repo}}/blob/{{branch}}{{current_dir}}" rel="nofollow">
							Download 
							<span aria-hidden="true" class="icon icon-caret-right">
								<svg viewBox="0 0 192 512" xmlns="http://www.w3.org/2000/svg">
									<path d="M0 384.662V127.338c0-17.818 21.543-26.741 34.142-14.142l128.662 128.662c7.81 7.81 7.81 20.474 0 28.284L34.142 398.804C21.543 411.404 0 402.48 0 384.662z"></path>
								</svg>
							</span>
						</a>
					</p>
				</div>
				{{/if}}
			</div>
		</div>
	</div>
</div>
{{/inline}}
{{> layout}}

M templates/repository/file.html.hbs => templates/repository/file.html.hbs +1 -1
@@ 21,7 21,7 @@

						<a class="nav-link" href="/{{repo}}/log/{{branch}}{{current_dir}}">Log</a>
					</li>
					<li class="nav-item">
						<a class="nav-link" href="/{{repo}}/raw/{{branch}}{{current_dir}}">Blame</a>
						<a class="nav-link" href="/{{repo}}/blame/{{branch}}{{current_dir}}">Blame</a>
					</li>
					<li class="nav-item">
						<a class="nav-link" href="/{{repo}}/blob/{{branch}}{{current_dir}}">View Raw</a>