Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Conversation

@mWalrus
Copy link
Contributor

@mWalrus mWalrus commented Jun 18, 2025

Cherry picked commits from #391.

Todo

  • Status section items are not being rendered
  • Stash list section items are not being rendered
  • Log section items are not being rendered

Above are not working because they are not being assigned ItemData.

Supersedes #391.

@mWalrus mWalrus changed the title Feat/just in time rendering feat: just in time rendering Jun 18, 2025
mWalrus added 6 commits June 18, 2025 23:02
Something is very broken with diffing in this commit lol.

On the bright side; We are rendering the status items and commits again!
this method now returns a `Vec<Line>` instead of a single `Line`

NOTE: hunk highlighting is crashing in this commit for some reason :(
@mWalrus
Copy link
Contributor Author

mWalrus commented Jun 18, 2025

Crashing somewhere here at the moment:

gitu/src/screen/mod.rs

Lines 330 to 373 in 1d1a7a7

fn render(self, area: Rect, buf: &mut Buffer) {
let style = &self.config.style;
for (line_index, line) in self.line_views(area.as_size()).enumerate() {
let line_area = Rect {
x: 0,
y: line_index as u16,
width: buf.area.width,
height: 1,
};
let indented_line_area = Rect { x: 1, ..line_area };
if line.highlighted {
buf.set_style(line_area, &style.selection_area);
if self.line_index[self.cursor] == line.item_index {
buf.set_style(line_area, &style.selection_line);
} else {
buf[(0, line_index as u16)]
.set_char(style.selection_bar.symbol)
.set_style(&style.selection_bar);
}
}
let line_width = line.display.width();
line.display.render(indented_line_area, buf);
let overflow = line_width > line_area.width as usize;
let line_width = line_width as u16;
if self.is_collapsed(line.item) && line_width > 0 || overflow {
let line_end = (indented_line_area.x + line_width).min(area.width - 1);
buf[(line_end, line_index as u16)].set_char('…');
}
if self.line_index[self.cursor] == line.item_index {
buf[(0, line_index as u16)]
.set_char(style.cursor.symbol)
.set_style(&style.cursor);
}
}
}

ERROR: index outside of buffer: the area is Rect { x: 0, y: 0, width: 213, height: 49 } but index is (0, 49)

Reading errors is really tricky currently, maybe I should implement a panic hook that logs the stacktrace to a file somewhere in a separate PR. What do you think about that @altsem ?

@mWalrus
Copy link
Contributor Author

mWalrus commented Jun 19, 2025

I've been hacking away some more and the current problem is rendering HunkLines.
In master we construct each display when the HunkLine is created after highlight::highlight_hunk_lines has done its thing.

We have since moved over highlighting to the render step but the problem is now that we need to rework src/highlight.rs to support line by line highlighting. This is so that we only have to highlight, style and render the hunk lines that are visible instead of the entire diff.

I skimmed through src/highlight.rs a little to see what could be done but I haven't come up with anything yet.

HunkLines are currently rendered unhighlighted.

@altsem
Copy link
Owner

altsem commented Jun 20, 2025

@mWalrus,

Panic hook

I realize this used to exist, seems I dropped it (by mistake?) in 3cb28c6.
But yea that sounds like a good idea. I hope that would give nice context from this error though. A lot of errors are handled explicitly via a Res, and they lose some context in this manner.

Highlighting

It does become tricky to highlight just a single line, since the highlighting is contextual. Now, it has some limited context actually -just a single hunk at a time.
Gitu used to syntax-highlight the entire file in a previous version even.

My idea was that if there was some way to cache/memoize highlighted lines, the code constructing the screen could just be asking

  • "Give me a line 3 of hunk 1, highlighted".
  • "Give me a line 4 of hunk 1, highlighted".
  • etc..

Now some caching/memoizing middle layer could then check if it has hunk 1 in store. And then either:

  • Highlight hunk 1, store it and return the line
  • Read hunk 1 from store and return the line

But in practice, using something like https://github.com/jaemk/cached could be neat:

#[cached]
pub(crate) fn highlight_hunk<'a>(
    config: &'a Config,
    diff: &'a Rc<Diff>,
    file_i: usize,
    hunk_i: usize,
) -> HunkHighlights {

struct HunkHighlights {
    spans: Vec<(Range<usize>, Style)>,
    line_index: Vec<usize, Range<usize>>,
}

impl HunkHighlights {
    fn get_line_spans(&self, line: usize) -> &[(Range<usize>, Style)] {
        let line_range = self.line_index[line];
        spans[line_range]
    }
}

Consider these some suggestions.

@mWalrus
Copy link
Contributor Author

mWalrus commented Jun 22, 2025

@altsem,

Syntax highlighting

Had never heard of cached before but it looked pretty fitting so I tried my hand at implementing some caching for syntax highlighting and it seems to be working! I'm currently failing a bunch of tests but most of the original functionality seems to be restored. Will take a closer look at tests next.

One thing I've also noticed is that even if we don't highlight the diffs immediately anymore, we parse and prepare diffs for highlighting even if they are not being rendered on the screen. Not sure if this means that they'll get processed for syntax highlighting anyways because of how Items are being taken care of when drawing a frame but I fear that this might be the case.

Panic hook

I've moved discussion to #393 :)

@altsem
Copy link
Owner

altsem commented Jun 24, 2025

@mWalrus i made some hashes for other purposes the other day here :) https://github.com/altsem/gitu/blob/master/src/items.rs#L126

It's needed to populate Item.id.
Which is used to identify collapsed/expanded sections.

Perhaps there's a way to compute it just once? Else it might not be too expensive to call again

@mWalrus
Copy link
Contributor Author

mWalrus commented Jun 24, 2025

@mWalrus i made some hashes for other purposes the other day here :) https://github.com/altsem/gitu/blob/master/src/items.rs#L126

It's needed to populate Item.id. Which is used to identify collapsed/expanded sections.

Perhaps there's a way to compute it just once? Else it might not be too expensive to call again

@altsem,
Ohh, yeah okay, that's cool! We should use those then. I can just take Item.id as a parameter for ItemData::to_line which would mean we won't have to compute anything more than we were already doing before!

Copy link
Owner

@altsem altsem left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Checking in on the progress and dumped some thoughts.
I think this is coming along really well. Feels like pieces are falling into place.

Will try poke around some more soon again

@mWalrus mWalrus marked this pull request as ready for review June 26, 2025 08:08
Copy link
Owner

@altsem altsem left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Found just one potential issue with the collapsed section config.
I'm curious how this will perform, if it solves that nasty lag in larger projects.

),
format!(" ({})", diff.file_diffs.len()).into(),
]),
id: hash(section),
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This might break what was implemented back in #276.
As seen here in the default config too:

# Sets initially collapsed sections in the editor. e.g.:
# collapsed_sections = ["untracked", "recent_commits", "branch_status"]
collapsed_sections = []

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@altsem,
I'm not really liking how we were passing around strings for section headers as it makes matching more brittle in my opinion, hence why I went with an enum.
Restoring this is no problem if keeping the old behavior is required but I'd love to rework this slightly so it's easier to maintain while keeping original behavior, if that's not out of scope.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I made an attempt in f732d3e.

@codecov

This comment was marked as spam.

@altsem
Copy link
Owner

altsem commented Jun 26, 2025

Oh, and, I reviewed everything and think we're basically good to go with the aforementioned things fixed!

The changelog generation is currently based on git commits, but I'm considering trashing it and doing it manually at some point...
https://github.com/altsem/gitu/blob/master/docs/dev-tooling.md

Or if we squash everything in here. 🤔

> git cliff --unreleased
# Changelog

All notable changes to this project will be documented in this file.

## [unreleased]

### 🚀 Features

- Cache syntax highlighting for hunks
- `ItemData::to_line` -> `ItemData::to_lines`
- Implement commit rendering
- JIT rendering
- Ability to rename a remote
- Switch terminal backend from Crossterm to Termwiz

### 🐛 Bug Fixes

- Don't format!() hunk highlight cache-keys
- Use already computed hunk hash for caching
- Return commits from `selected_rev` again
- Restore ref styling
- Replace tab-characters with 4 spaces again
- Generate more collision resistant cache-key
- Restore stash item styling
- `Diff::hunk_content` to unify getting hunk content
- Restore creation of `RefKind::Remote` items
- Store visual reference prefix to apply later
- Unify hunk line range scanning into one fn
- Typo in stash string
- Restore status section headers
- Restore commit view header
- `ItemData::Raw` holds an owned String
- `ItemData::to_line` now produces a single line
- (mostly) restore hunk line rendering
- Correctly highlight hunks again
- Removing very hacky logging that made it thru
- Make `Item.data` is no longer optional
- Adding some missing section headers
- Construct lines again
- Restore replace of TAB with 4 spaces
- Typo in branch status text

### ⚡ Performance

- Avoid some allocations
- More efficiently keep track of changes between updates

<!-- generated by git-cliff -->

@mWalrus
Copy link
Contributor Author

mWalrus commented Jun 26, 2025

@altsem,
We can squash the commits to make it easier. We can reword it so it makes more sense as well! 😊

@altsem altsem enabled auto-merge (squash) June 27, 2025 16:05
@altsem
Copy link
Owner

altsem commented Jun 27, 2025

I'll merge this, let it sit in master for a few days, perhaps bundle some more stuff in, then include it in a release soon :) gj!

@altsem altsem disabled auto-merge June 27, 2025 16:08
@altsem altsem merged commit 8ad7fb9 into altsem:master Jun 27, 2025
5 checks passed
@mWalrus mWalrus deleted the feat/just-in-time-rendering branch July 1, 2025 11:19
@jonathanj
Copy link
Contributor

This is a great improvement, I recently had a case where highlighting (a brutal JSON file) caused a lot of slowdown, so thanks!

The changelog generation is currently based on git commits, but I'm considering trashing it and doing it manually at some point... https://github.com/altsem/gitu/blob/master/docs/dev-tooling.md

Or if we squash everything in here. 🤔

I noticed this, and thought I'd point out that you could configure git-cliff to filter based on merge commit, so if you discard non-merge commits then as long as your merge commit is conventional, it would be a "one PR equals one changelog item" workflow.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants