Skip to content

Commit

Permalink
store document and diff base in Diff
Browse files Browse the repository at this point in the history
  • Loading branch information
pascalkuthe committed Jan 10, 2023
1 parent 0dbee95 commit 0223877
Show file tree
Hide file tree
Showing 5 changed files with 63 additions and 26 deletions.
58 changes: 42 additions & 16 deletions helix-vcs/src/diff.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,11 +28,18 @@ struct Event {
render_lock: Option<RenderLock>,
}

#[derive(Clone, Debug, Default)]
struct DiffInner {
diff_base: Rope,
doc: Rope,
hunks: Vec<Hunk>,
}

#[derive(Clone, Debug)]
pub struct DiffHandle {
channel: UnboundedSender<Event>,
render_lock: Arc<RwLock<()>>,
hunks: Arc<Mutex<Vec<Hunk>>>,
diff: Arc<Mutex<DiffInner>>,
inverted: bool,
}

Expand All @@ -47,18 +54,18 @@ impl DiffHandle {
redraw_handle: RedrawHandle,
) -> (DiffHandle, JoinHandle<()>) {
let (sender, receiver) = unbounded_channel();
let hunks: Arc<Mutex<Vec<Hunk>>> = Arc::default();
let diff: Arc<Mutex<DiffInner>> = Arc::default();
let worker = DiffWorker {
channel: receiver,
hunks: hunks.clone(),
diff: diff.clone(),
new_hunks: Vec::default(),
redraw_notify: redraw_handle.0,
diff_finished_notify: Arc::default(),
};
let handle = tokio::spawn(worker.run(diff_base, doc));
let differ = DiffHandle {
channel: sender,
hunks,
diff,
inverted: false,
render_lock: redraw_handle.1,
};
Expand All @@ -69,9 +76,9 @@ impl DiffHandle {
self.inverted = !self.inverted;
}

pub fn hunks(&self) -> FileHunks {
FileHunks {
hunks: self.hunks.lock(),
pub fn read(&self) -> Diff {
Diff {
diff: self.diff.lock(),
inverted: self.inverted,
}
}
Expand Down Expand Up @@ -168,28 +175,44 @@ impl Hunk {
/// A list of changes in a file sorted in ascending
/// non-overlapping order
#[derive(Debug)]
pub struct FileHunks<'a> {
hunks: MutexGuard<'a, Vec<Hunk>>,
pub struct Diff<'a> {
diff: MutexGuard<'a, DiffInner>,
inverted: bool,
}

impl FileHunks<'_> {
impl Diff<'_> {
pub fn diff_base(&self) -> &Rope {
if self.inverted {
&self.diff.doc
} else {
&self.diff.diff_base
}
}

pub fn doc(&self) -> &Rope {
if self.inverted {
&self.diff.diff_base
} else {
&self.diff.doc
}
}

pub fn is_inverted(&self) -> bool {
self.inverted
}

/// Returns the `Hunk` for the `n`th change in this file.
/// if there is no `n`th change `Hunk::NONE` is returned instead.
pub fn nth_hunk(&self, n: u32) -> Hunk {
match self.hunks.get(n as usize) {
match self.diff.hunks.get(n as usize) {
Some(hunk) if self.inverted => hunk.invert(),
Some(hunk) => hunk.clone(),
None => Hunk::NONE,
}
}

pub fn len(&self) -> u32 {
self.hunks.len() as u32
self.diff.hunks.len() as u32
}

pub fn is_empty(&self) -> bool {
Expand All @@ -204,19 +227,20 @@ impl FileHunks<'_> {
};

let res = self
.diff
.hunks
.binary_search_by_key(&line, |hunk| hunk_range(hunk).start);

match res {
// Search found a hunk that starts exactly at this line, return the next hunk if it exists.
Ok(pos) if pos + 1 == self.hunks.len() => None,
Ok(pos) if pos + 1 == self.diff.hunks.len() => None,
Ok(pos) => Some(pos as u32 + 1),

// No hunk starts exactly at this line, so the search returns
// the position where a hunk starting at this line should be inserted.
// That position is exactly the position of the next hunk or the end
// of the list if no such hunk exists
Err(pos) if pos == self.hunks.len() => None,
Err(pos) if pos == self.diff.hunks.len() => None,
Err(pos) => Some(pos as u32),
}
}
Expand All @@ -228,6 +252,7 @@ impl FileHunks<'_> {
|hunk: &Hunk| hunk.after.clone()
};
let res = self
.diff
.hunks
.binary_search_by_key(&line, |hunk| hunk_range(hunk).end);

Expand All @@ -237,7 +262,7 @@ impl FileHunks<'_> {
// which represents a pure removal.
// Removals are technically empty but are still shown as single line hunks
// and as such we must jump to the previous hunk (if it exists) if we are already inside the removal
Ok(pos) if !hunk_range(&self.hunks[pos]).is_empty() => Some(pos as u32),
Ok(pos) if !hunk_range(&self.diff.hunks[pos]).is_empty() => Some(pos as u32),

// No hunk ends exactly at this line, so the search returns
// the position where a hunk ending at this line should be inserted.
Expand All @@ -255,6 +280,7 @@ impl FileHunks<'_> {
};

let res = self
.diff
.hunks
.binary_search_by_key(&line, |hunk| hunk_range(hunk).start);

Expand All @@ -267,7 +293,7 @@ impl FileHunks<'_> {
// The previous hunk contains this hunk if it exists and doesn't end before this line
Err(0) => None,
Err(pos) => {
let hunk = hunk_range(&self.hunks[pos - 1]);
let hunk = hunk_range(&self.diff.hunks[pos - 1]);
if hunk.end > line || include_removal && hunk.start == line && hunk.is_empty() {
Some(pos as u32 - 1)
} else {
Expand Down
8 changes: 8 additions & 0 deletions helix-vcs/src/diff/line_cache.rs
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,14 @@ impl InternedRopeLines {
res
}

pub fn doc(&self) -> Rope {
self.doc.clone()
}

pub fn diff_base(&self) -> Rope {
self.diff_base.clone()
}

/// Updates the `diff_base` and optionally the document if `doc` is not None
pub fn update_diff_base(&mut self, diff_base: Rope, doc: Option<Rope>) {
self.interned.clear();
Expand Down
15 changes: 9 additions & 6 deletions helix-vcs/src/diff/worker.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ use tokio::sync::Notify;
use tokio::time::{timeout, timeout_at, Duration};

use crate::diff::{
Event, RenderLock, ALGORITHM, DIFF_DEBOUNCE_TIME_ASYNC, DIFF_DEBOUNCE_TIME_SYNC,
DiffInner, Event, RenderLock, ALGORITHM, DIFF_DEBOUNCE_TIME_ASYNC, DIFF_DEBOUNCE_TIME_SYNC,
};

use super::line_cache::InternedRopeLines;
Expand All @@ -21,7 +21,7 @@ mod test;

pub(super) struct DiffWorker {
pub channel: UnboundedReceiver<Event>,
pub hunks: Arc<Mutex<Vec<Hunk>>>,
pub diff: Arc<Mutex<DiffInner>>,
pub new_hunks: Vec<Hunk>,
pub redraw_notify: Arc<Notify>,
pub diff_finished_notify: Arc<Notify>,
Expand All @@ -46,7 +46,7 @@ impl DiffWorker {
if let Some(lines) = interner.interned_lines() {
self.perform_diff(lines);
}
self.apply_hunks();
self.apply_hunks(interner.diff_base(), interner.doc());
while let Some(event) = self.channel.recv().await {
let (doc, diff_base) = self.accumulate_events(event).await;

Expand All @@ -70,15 +70,18 @@ impl DiffWorker {
#[cfg(not(test))]
tokio::task::block_in_place(process_accumulated_events);

self.apply_hunks();
self.apply_hunks(interner.diff_base(), interner.doc());
}
}

/// update the hunks (used by the gutter) by replacing it with `self.new_hunks`.
/// `self.new_hunks` is always empty after this function runs.
/// To improve performance this function tries to reuse the allocation of the old diff previously stored in `self.line_diffs`
fn apply_hunks(&mut self) {
swap(&mut *self.hunks.lock(), &mut self.new_hunks);
fn apply_hunks(&mut self, diff_base: Rope, doc: Rope) {
let mut diff = self.diff.lock();
diff.diff_base = diff_base;
diff.doc = doc;
swap(&mut diff.hunks, &mut self.new_hunks);
self.diff_finished_notify.notify_waiters();
self.new_hunks.clear();
}
Expand Down
6 changes: 3 additions & 3 deletions helix-vcs/src/diff/worker/test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,12 @@ impl DiffHandle {
)
}
async fn into_diff(self, handle: JoinHandle<()>) -> Vec<Hunk> {
let hunks = self.hunks;
let diff = self.diff;
// dropping the channel terminates the task
drop(self.channel);
handle.await.unwrap();
let hunks = hunks.lock();
Vec::clone(&*hunks)
let diff = diff.lock();
Vec::clone(&diff.hunks)
}
}

Expand Down
2 changes: 1 addition & 1 deletion helix-view/src/gutter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,7 @@ pub fn diff<'doc>(
let deleted = theme.get("diff.minus");
let modified = theme.get("diff.delta");
if let Some(diff_handle) = doc.diff_handle() {
let hunks = diff_handle.hunks();
let hunks = diff_handle.read();
let mut hunk_i = 0;
let mut hunk = hunks.nth_hunk(hunk_i);
Box::new(move |line: usize, _selected: bool, out: &mut String| {
Expand Down

0 comments on commit 0223877

Please sign in to comment.