Skip to content

Commit c07e7ef

Browse files
author
Luctius
committed
Shows line changes relative to VCS
In this case git, but others are easily added.
1 parent 0aa4390 commit c07e7ef

File tree

10 files changed

+282
-0
lines changed

10 files changed

+282
-0
lines changed

Cargo.lock

Lines changed: 71 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ members = [
66
"helix-tui",
77
"helix-syntax",
88
"helix-lsp",
9+
"helix-vcs",
910
]
1011

1112
# Build helix-syntax in release mode to make the code path faster in development.

helix-term/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ path = "src/main.rs"
2525
helix-core = { version = "0.3", path = "../helix-core" }
2626
helix-view = { version = "0.3", path = "../helix-view" }
2727
helix-lsp = { version = "0.3", path = "../helix-lsp" }
28+
helix-vcs = { version = "0.3", path = "../helix-vcs"}
2829

2930
anyhow = "1"
3031
once_cell = "1.8"

helix-term/src/ui/editor.rs

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ use helix_core::{
1212
syntax::{self, HighlightEvent},
1313
LineEnding, Position, Range,
1414
};
15+
use helix_vcs::LineChange;
1516
use helix_view::{
1617
document::Mode,
1718
graphics::{CursorKind, Modifier, Rect, Style},
@@ -327,8 +328,28 @@ impl EditorView {
327328
let info: Style = theme.get("info");
328329
let hint: Style = theme.get("hint");
329330

331+
let line_added: Style = theme.get("line_added");
332+
let line_removed: Style = theme.get("line_removed");
333+
let line_modified: Style = theme.get("line_modified");
334+
330335
for (i, line) in (view.first_line..last_line).enumerate() {
331336
use helix_core::diagnostic::Severity;
337+
if let Some(line_changes) = doc.vcs_line_changes() {
338+
if let Some(line_change) = line_changes.get(&(line + 1)) {
339+
surface.set_stringn(
340+
viewport.x - OFFSET,
341+
viewport.y + i as u16,
342+
// TODO: set this in a theme
343+
line_change.as_str(),
344+
1,
345+
match line_change {
346+
LineChange::Added => line_added,
347+
LineChange::RemovedAbove | LineChange::RemovedBelow => line_removed,
348+
LineChange::Modified => line_modified,
349+
},
350+
);
351+
}
352+
}
332353
if let Some(diagnostic) = doc.diagnostics().iter().find(|d| d.line == line) {
333354
surface.set_stringn(
334355
viewport.x - OFFSET,

helix-vcs/Cargo.toml

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
[package]
2+
name = "helix-vcs"
3+
version = "0.3.0"
4+
edition = "2018"
5+
6+
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
7+
8+
[dependencies]
9+
git2 = { version = "0.13", default-features = false }
10+
helix-core = { version = "0.3", path = "../helix-core" }

helix-vcs/src/git.rs

Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
use std::collections::HashMap;
2+
use std::fs;
3+
use std::path::{Path, PathBuf};
4+
5+
use git2::{DiffOptions, IntoCString, Repository};
6+
7+
use crate::{LineChange, LineChanges};
8+
9+
pub struct Git {
10+
relative_path: PathBuf,
11+
repo: Repository,
12+
pub line_changes: Option<LineChanges>,
13+
}
14+
impl Git {
15+
pub fn from_path(filename: &Path) -> Option<Self> {
16+
if let Ok(repo) = Repository::discover(&filename) {
17+
let repo_path_absolute = fs::canonicalize(repo.workdir()?).ok()?;
18+
19+
let absolute_path = fs::canonicalize(&filename).ok()?.to_path_buf();
20+
let relative_path = absolute_path
21+
.strip_prefix(&repo_path_absolute)
22+
.ok()?
23+
.to_path_buf();
24+
Some(Git {
25+
repo,
26+
relative_path,
27+
line_changes: None,
28+
})
29+
} else {
30+
None
31+
}
32+
}
33+
34+
/// Taken from https://github.com/sharkdp/bat/blob/master/src/diff.rs
35+
pub fn diff(&mut self) {
36+
let mut diff_options = DiffOptions::new();
37+
let pathspec = if let Ok(p) = self.relative_path.clone().into_c_string() {
38+
p
39+
} else {
40+
return;
41+
};
42+
diff_options.pathspec(pathspec);
43+
diff_options.context_lines(0);
44+
45+
let diff = if let Ok(d) = self
46+
.repo
47+
.diff_index_to_workdir(None, Some(&mut diff_options))
48+
{
49+
d
50+
} else {
51+
return;
52+
};
53+
54+
let mut line_changes: LineChanges = HashMap::new();
55+
56+
let mark_section =
57+
|line_changes: &mut LineChanges, start: u32, end: i32, change: LineChange| {
58+
for line in start..=end as u32 {
59+
line_changes.insert(line as usize, change);
60+
}
61+
};
62+
63+
let _ = diff.foreach(
64+
&mut |_, _| true,
65+
None,
66+
Some(&mut |delta, hunk| {
67+
let path = delta.new_file().path().unwrap_or_else(|| Path::new(""));
68+
69+
if self.relative_path != path {
70+
return false;
71+
}
72+
73+
let old_lines = hunk.old_lines();
74+
let new_start = hunk.new_start();
75+
let new_lines = hunk.new_lines();
76+
let new_end = (new_start + new_lines) as i32 - 1;
77+
78+
if old_lines == 0 && new_lines > 0 {
79+
mark_section(&mut line_changes, new_start, new_end, LineChange::Added);
80+
} else if new_lines == 0 && old_lines > 0 {
81+
if new_start == 0 {
82+
mark_section(&mut line_changes, 1, 1, LineChange::RemovedAbove);
83+
} else {
84+
mark_section(
85+
&mut line_changes,
86+
new_start,
87+
new_start as i32,
88+
LineChange::RemovedBelow,
89+
);
90+
}
91+
} else {
92+
mark_section(&mut line_changes, new_start, new_end, LineChange::Modified);
93+
}
94+
95+
true
96+
}),
97+
None,
98+
);
99+
100+
self.line_changes = Some(line_changes);
101+
}
102+
}
103+

helix-vcs/src/lib.rs

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
use std::collections::HashMap;
2+
use std::path::Path;
3+
4+
mod git;
5+
use git::Git;
6+
7+
#[derive(Copy, Clone, Debug)]
8+
pub enum LineChange {
9+
Added,
10+
RemovedAbove,
11+
RemovedBelow,
12+
Modified,
13+
}
14+
impl LineChange {
15+
pub fn as_str(&self) -> &'static str {
16+
match self {
17+
LineChange::Added => &"▌",
18+
LineChange::RemovedAbove => &"▘",
19+
LineChange::RemovedBelow => &"▖",
20+
LineChange::Modified => &"▐",
21+
}
22+
}
23+
}
24+
25+
pub type LineChanges = HashMap<usize, LineChange>;
26+
27+
//#[derive(Clone)]
28+
pub enum VCS {
29+
Git(Git),
30+
}
31+
impl VCS {
32+
pub fn from_path(filename: &Path) -> Option<Self> {
33+
Some(VCS::Git(Git::from_path(filename)?))
34+
}
35+
pub fn get_line_changes(&self) -> Option<&LineChanges> {
36+
match self {
37+
VCS::Git(git) => git.line_changes.as_ref(),
38+
}
39+
}
40+
pub fn diff(&mut self) {
41+
match self {
42+
VCS::Git(git) => git.diff(),
43+
}
44+
}
45+
}

helix-view/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ bitflags = "1.0"
1818
anyhow = "1"
1919
helix-core = { version = "0.3", path = "../helix-core" }
2020
helix-lsp = { version = "0.3", path = "../helix-lsp"}
21+
helix-vcs = { version = "0.3", path = "../helix-vcs"}
2122
crossterm = { version = "0.20", optional = true }
2223

2324
# Conversion traits

0 commit comments

Comments
 (0)