Skip to content

Commit 5a3ff74

Browse files
pascalkuthearchseerthe-mikedavis
authored
Show (git) diff signs in gutter (#3890)
* Show (git) diff signs in gutter (#3890) Avoid string allocation when git diffing Incrementally diff using changesets refactor diffs to be provider indepndent and improve git implementation remove dependency on zlib-ng switch to asynchronus diffing with similar Update helix-vcs/Cargo.toml fix toml formatting Co-authored-by: Ivan Tham <[email protected]> fix typo in documentation use ropey reexpors from helix-core fix crash when creating new file remove useless use if io::Cursor fix spelling mistakes implement suggested improvement to repository loading improve git test isolation remove lefover comments Co-authored-by: univerz <[email protected]> fixed spelling mistake minor cosmetic changes fix: set self.differ to None if decoding the diff_base fails fixup formatting Co-authored-by: Ivan Tham <[email protected]> reload diff_base when file is reloaded from disk switch to imara-diff Fixup formatting Co-authored-by: Blaž Hrastnik <[email protected]> Redraw buffer whenever a diff is updated. Only store hunks instead of changes for individual lines to easily allow jumping between them Update to latest gitoxide version Change default diff gutter position Only update gutter after timeout * update diff gutter synchronously, with a timeout * Apply suggestions from code review Co-authored-by: Blaž Hrastnik <[email protected]> Co-authored-by: Michael Davis <[email protected]> * address review comments and ensure lock is always aquired * remove configuration for redraw timeout Co-authored-by: Blaž Hrastnik <[email protected]> Co-authored-by: Michael Davis <[email protected]>
1 parent 67415e0 commit 5a3ff74

21 files changed

+2036
-76
lines changed

Cargo.lock

+868-10
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

+1
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ members = [
77
"helix-lsp",
88
"helix-dap",
99
"helix-loader",
10+
"helix-vcs",
1011
"xtask",
1112
]
1213

book/src/configuration.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ on unix operating systems.
4646
| `line-number` | Line number display: `absolute` simply shows each line's number, while `relative` shows the distance from the current line. When unfocused or in insert mode, `relative` will still show absolute line numbers. | `absolute` |
4747
| `cursorline` | Highlight all lines with a cursor. | `false` |
4848
| `cursorcolumn` | Highlight all columns with a cursor. | `false` |
49-
| `gutters` | Gutters to display: Available are `diagnostics` and `line-numbers` and `spacer`, note that `diagnostics` also includes other features like breakpoints, 1-width padding will be inserted if gutters is non-empty | `["diagnostics", "spacer", "line-numbers"]` |
49+
| `gutters` | Gutters to display: Available are `diagnostics` and `diff` and `line-numbers` and `spacer`, note that `diagnostics` also includes other features like breakpoints, 1-width padding will be inserted if gutters is non-empty | `["diagnostics", "spacer", "line-numbers", "spacer", "diff"]` |
5050
| `auto-completion` | Enable automatic pop up of auto-completion. | `true` |
5151
| `auto-format` | Enable automatic formatting on save. | `true` |
5252
| `auto-save` | Enable automatic saving on focus moving away from Helix. Requires [focus event support](https://github.com/helix-editor/helix/wiki/Terminal-Support) from your terminal. | `false` |

helix-core/src/lib.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,7 @@ pub fn find_root(root: Option<&str>, root_markers: &[String]) -> std::path::Path
8383
top_marker.map_or(current_dir, |a| a.to_path_buf())
8484
}
8585

86-
pub use ropey::{str_utils, Rope, RopeBuilder, RopeSlice};
86+
pub use ropey::{self, str_utils, Rope, RopeBuilder, RopeSlice};
8787

8888
// pub use tendril::StrTendril as Tendril;
8989
pub use smartstring::SmartString;

helix-term/Cargo.toml

+3
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,10 @@ build = true
1717
app = true
1818

1919
[features]
20+
default = ["git"]
2021
unicode-lines = ["helix-core/unicode-lines"]
2122
integration = []
23+
git = ["helix-vcs/git"]
2224

2325
[[bin]]
2426
name = "hx"
@@ -29,6 +31,7 @@ helix-core = { version = "0.6", path = "../helix-core" }
2931
helix-view = { version = "0.6", path = "../helix-view" }
3032
helix-lsp = { version = "0.6", path = "../helix-lsp" }
3133
helix-dap = { version = "0.6", path = "../helix-dap" }
34+
helix-vcs = { version = "0.6", path = "../helix-vcs" }
3235
helix-loader = { version = "0.6", path = "../helix-loader" }
3336

3437
anyhow = "1"

helix-term/src/application.rs

+32-18
Original file line numberDiff line numberDiff line change
@@ -274,16 +274,27 @@ impl Application {
274274
}
275275

276276
#[cfg(feature = "integration")]
277-
fn render(&mut self) {}
277+
async fn render(&mut self) {}
278278

279279
#[cfg(not(feature = "integration"))]
280-
fn render(&mut self) {
280+
async fn render(&mut self) {
281281
let mut cx = crate::compositor::Context {
282282
editor: &mut self.editor,
283283
jobs: &mut self.jobs,
284284
scroll: None,
285285
};
286286

287+
// Acquire mutable access to the redraw_handle lock
288+
// to ensure that there are no tasks running that want to block rendering
289+
drop(cx.editor.redraw_handle.1.write().await);
290+
cx.editor.needs_redraw = false;
291+
{
292+
// exhaust any leftover redraw notifications
293+
let notify = cx.editor.redraw_handle.0.notified();
294+
tokio::pin!(notify);
295+
notify.enable();
296+
}
297+
287298
let area = self
288299
.terminal
289300
.autoresize()
@@ -304,7 +315,7 @@ impl Application {
304315
where
305316
S: Stream<Item = crossterm::Result<crossterm::event::Event>> + Unpin,
306317
{
307-
self.render();
318+
self.render().await;
308319
self.last_render = Instant::now();
309320

310321
loop {
@@ -329,18 +340,18 @@ impl Application {
329340
biased;
330341

331342
Some(event) = input_stream.next() => {
332-
self.handle_terminal_events(event);
343+
self.handle_terminal_events(event).await;
333344
}
334345
Some(signal) = self.signals.next() => {
335346
self.handle_signals(signal).await;
336347
}
337348
Some(callback) = self.jobs.futures.next() => {
338349
self.jobs.handle_callback(&mut self.editor, &mut self.compositor, callback);
339-
self.render();
350+
self.render().await;
340351
}
341352
Some(callback) = self.jobs.wait_futures.next() => {
342353
self.jobs.handle_callback(&mut self.editor, &mut self.compositor, callback);
343-
self.render();
354+
self.render().await;
344355
}
345356
event = self.editor.wait_event() => {
346357
let _idle_handled = self.handle_editor_event(event).await;
@@ -445,25 +456,25 @@ impl Application {
445456
self.compositor.resize(area);
446457
self.terminal.clear().expect("couldn't clear terminal");
447458

448-
self.render();
459+
self.render().await;
449460
}
450461
signal::SIGUSR1 => {
451462
self.refresh_config();
452-
self.render();
463+
self.render().await;
453464
}
454465
_ => unreachable!(),
455466
}
456467
}
457468

458-
pub fn handle_idle_timeout(&mut self) {
469+
pub async fn handle_idle_timeout(&mut self) {
459470
let mut cx = crate::compositor::Context {
460471
editor: &mut self.editor,
461472
jobs: &mut self.jobs,
462473
scroll: None,
463474
};
464475
let should_render = self.compositor.handle_event(&Event::IdleTimeout, &mut cx);
465-
if should_render {
466-
self.render();
476+
if should_render || self.editor.needs_redraw {
477+
self.render().await;
467478
}
468479
}
469480

@@ -536,31 +547,31 @@ impl Application {
536547
match event {
537548
EditorEvent::DocumentSaved(event) => {
538549
self.handle_document_write(event);
539-
self.render();
550+
self.render().await;
540551
}
541552
EditorEvent::ConfigEvent(event) => {
542553
self.handle_config_events(event);
543-
self.render();
554+
self.render().await;
544555
}
545556
EditorEvent::LanguageServerMessage((id, call)) => {
546557
self.handle_language_server_message(call, id).await;
547558
// limit render calls for fast language server messages
548559
let last = self.editor.language_servers.incoming.is_empty();
549560

550561
if last || self.last_render.elapsed() > LSP_DEADLINE {
551-
self.render();
562+
self.render().await;
552563
self.last_render = Instant::now();
553564
}
554565
}
555566
EditorEvent::DebuggerEvent(payload) => {
556567
let needs_render = self.editor.handle_debugger_message(payload).await;
557568
if needs_render {
558-
self.render();
569+
self.render().await;
559570
}
560571
}
561572
EditorEvent::IdleTimer => {
562573
self.editor.clear_idle_timer();
563-
self.handle_idle_timeout();
574+
self.handle_idle_timeout().await;
564575

565576
#[cfg(feature = "integration")]
566577
{
@@ -572,7 +583,10 @@ impl Application {
572583
false
573584
}
574585

575-
pub fn handle_terminal_events(&mut self, event: Result<CrosstermEvent, crossterm::ErrorKind>) {
586+
pub async fn handle_terminal_events(
587+
&mut self,
588+
event: Result<CrosstermEvent, crossterm::ErrorKind>,
589+
) {
576590
let mut cx = crate::compositor::Context {
577591
editor: &mut self.editor,
578592
jobs: &mut self.jobs,
@@ -596,7 +610,7 @@ impl Application {
596610
};
597611

598612
if should_redraw && !self.editor.should_close() {
599-
self.render();
613+
self.render().await;
600614
}
601615
}
602616

helix-term/src/commands/typed.rs

+7-4
Original file line numberDiff line numberDiff line change
@@ -1028,10 +1028,12 @@ fn reload(
10281028
}
10291029

10301030
let scrolloff = cx.editor.config().scrolloff;
1031+
let redraw_handle = cx.editor.redraw_handle.clone();
10311032
let (view, doc) = current!(cx.editor);
1032-
doc.reload(view).map(|_| {
1033-
view.ensure_cursor_in_view(doc, scrolloff);
1034-
})
1033+
doc.reload(view, &cx.editor.diff_providers, redraw_handle)
1034+
.map(|_| {
1035+
view.ensure_cursor_in_view(doc, scrolloff);
1036+
})
10351037
}
10361038

10371039
fn reload_all(
@@ -1066,7 +1068,8 @@ fn reload_all(
10661068

10671069
// Every doc is guaranteed to have at least 1 view at this point.
10681070
let view = view_mut!(cx.editor, view_ids[0]);
1069-
doc.reload(view)?;
1071+
let redraw_handle = cx.editor.redraw_handle.clone();
1072+
doc.reload(view, &cx.editor.diff_providers, redraw_handle)?;
10701073

10711074
for view_id in view_ids {
10721075
let view = view_mut!(cx.editor, view_id);

helix-term/src/ui/editor.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -730,7 +730,7 @@ impl EditorView {
730730
let mut text = String::with_capacity(8);
731731

732732
for gutter_type in view.gutters() {
733-
let gutter = gutter_type.style(editor, doc, view, theme, is_focused);
733+
let mut gutter = gutter_type.style(editor, doc, view, theme, is_focused);
734734
let width = gutter_type.width(view, doc);
735735
text.reserve(width); // ensure there's enough space for the gutter
736736
for (i, line) in (view.offset.row..(last_line + 1)).enumerate() {

helix-vcs/Cargo.toml

+28
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
[package]
2+
name = "helix-vcs"
3+
version = "0.6.0"
4+
authors = ["Blaž Hrastnik <[email protected]>"]
5+
edition = "2021"
6+
license = "MPL-2.0"
7+
categories = ["editor"]
8+
repository = "https://github.com/helix-editor/helix"
9+
homepage = "https://helix-editor.com"
10+
11+
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
12+
13+
[dependencies]
14+
helix-core = { version = "0.6", path = "../helix-core" }
15+
16+
tokio = { version = "1", features = ["rt", "rt-multi-thread", "time", "sync", "parking_lot", "macros"] }
17+
parking_lot = "0.12"
18+
19+
git-repository = { version = "0.26", default-features = false , optional = true }
20+
imara-diff = "0.1.5"
21+
22+
log = "0.4"
23+
24+
[features]
25+
git = ["git-repository"]
26+
27+
[dev-dependencies]
28+
tempfile = "3.3"

0 commit comments

Comments
 (0)