Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Syntect highlighting experiment #73

Draft
wants to merge 6 commits into
base: main
Choose a base branch
from

Conversation

CptPotato
Copy link

@CptPotato CptPotato commented Sep 22, 2023

Adds optional syntax highlighting based on syntect to hunk diffs.

screenshots

image

with background color:
image


Remaining things to fix/consider (not all changes are pushed to the branch, yet):

  • Use separate highlighter instance for added and removed lines to keep the syntax intact.
    • Highlighting hunks in isolation is incorrect.
    • Highlight the whole file first to ensure syntax in each hunk is correct.
  • Add inline highlighting for single line changes.
    • Works but breaks easily and I'm not happy with the code. I probably won't include it in this PR.
  • Don't feed the hunk marker @@...@@ into the highlighter.
  • Increase hunk header visibility. Right now it blends in with the highlighted code too much.
  • Try highlighting only the selected hunk for visual aid. Display others in gray.
    • Highlight all hunks when the cursor is on the file.
    • Make this optional.
  • Highlight untracked files.
  • Check for interference with the existing highlighting.
    • Highlight trailing whitespaces.
  • Add configuration options.
  • Try highlighting the background to the whole width of the terminal?
    • Probably "out of scope" for this PR.
  • Use syntax theme background color?
    • Requires highlighting to the full width of the terminal to look good, Probably "out of scope" for this PR.

@Piturnah
Copy link
Owner

Looking amazing so far, thanks for opening the PR!

Can't wait for this to be ready for review, if you stick with it.

- refactor highlighting.rs
- add parsed Hunk struct
- remove existing highlighting
@CptPotato
Copy link
Author

The code is starting to look a little better, though there's still a few things to do.


I made the syntax highlighting optional (still need to fix the config, though). When disabled it uses the current look.

Also, I tried to see how it looks when only the selected hunk is highlighted:

screenshot

image

I personally like it, what do you think? I could also see if I can make this optional.

Copy link
Owner

@Piturnah Piturnah left a comment

Choose a reason for hiding this comment

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

Left a few notes/questions! Don't mean to step on your toes too much since I know this is a draft PR. Really appreciate the work you're doing here.

Also, I tried to see how it looks when only the selected hunk is highlighted:

I'm a big fan of the look and feel of this. I do think this functionality should be configurable, but also I think it should be the default behaviour for now (time will tell).

I wonder if the whole file diff should be highlighted when the file is selected (rather than a hunk under the file) as well.

@@ -64,6 +65,8 @@ impl Default for Options {
lookahead_lines: 5,
truncate_lines: true,
ws_error_highlight: WsErrorHighlight::default(),
// TODO: decide on default
Copy link
Owner

Choose a reason for hiding this comment

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

Ideally, I think the default syntax highlighting theme should come from the terminal's theme (just as the default gex colour theme does). This should make life a bit easier for users as some colour coherency is ensured out of the box.

I am not sure of the feasibility of this since Syntect only accepts colors as RGBA. This StackOverflow answer suggests that in at least xterm you can query the current terminal colors, but I haven't investigated how cross-platform this approach is, and I don't think it's supported in Crossterm.

This will be a decent amount of work to look into and is definitely not blocking for this PR - but my gut feeling is that as long as this isn't possible, then syntax highlighting should be disabled by default. Let me know your thoughts.

Copy link
Author

Choose a reason for hiding this comment

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

I'm not sure if this is a viable solution since, as you mentioned there doesn't seem to be a reliable way to get the terminal's colors.

I think a "better" way could be creating a custom syntect theme and then map the Styles back to the 16 color ANSI sequenses and display them using crossterm. But I don't think I'll explore that option in this PR for now.

Either way, I agree that gex should be usable out-of-the-box even on terminals that don't support true-color / 24 bit colors. So yeah, the highlighting should be disabled by default.


// workaround: remove trailing line break
buf.pop();
buf.push_str("\x1b[0m");
Copy link
Owner

Choose a reason for hiding this comment

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

Haven't investigated but my suspicion is that resetting attributes this way will break things if the user has a custom background color set. The render::terminal module provides a ResetAttributes that should do what you want (if it doesn't then that's probably a bug with ResetAttributes).

buf.pop();

// according to docs of `as_24_bit_terminal_escaped`
buf.push_str("\x1b[0m");
Copy link
Owner

Choose a reason for hiding this comment

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

See prior comment

syntax: &SyntaxReference,
) -> String {
// TODO: move somewhere else?
let marker_added = as_24_bit_terminal_escaped(&[(MARKER_ADDED_STYLE, MARKER_ADDED)], true);
Copy link
Owner

Choose a reason for hiding this comment

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

It is unclear to me why we use as_24_bit_terminal_escaped instead of the crossterm Command API


#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum DiffLineType {
Unchanged,
Copy link
Owner

Choose a reason for hiding this comment

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

Bikeshed (not important): how do we feel about changing this variant to Context to bring it inline with git's documentation?

Copy link
Author

Choose a reason for hiding this comment

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

Sounds good, I'm not familiar with the exact naming in git so I just chose something. I'll also use "deleted" instead of "removed" since I think I saw these used in other parts of gex.

Copy link
Owner

Choose a reason for hiding this comment

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

I didn't pay it much thought before but come to think of it, git actually uses "old" and "new" where in gex we use "deletion" and "addition", so it's already inconsistent. To be honest old and new don't sound as obvious to me personally, could just be my bias.

Anyway, this is far from an important point about the code so I will stop fixating on it 😅

' ' => Ok(Self::Unchanged),
'+' => Ok(Self::Added),
'-' => Ok(Self::Removed),
c => Err(anyhow!("'{c}' is not a valid diff type character.")),
Copy link
Owner

Choose a reason for hiding this comment

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

In the case of an added trailing newline this will cause a bug (see #62)

@CptPotato
Copy link
Author

Thanks for the first review, appreciate it! I'll probably take my time with this PR.

One problem I didn't notice until now is that highlighting each hunk in isolation is wrong, since it doesn't always produce valid syntax.
So I'll have to perform highlighting to the whole file first and then take the highlighted lines for each hunk from there. I'll probably cut some corners for now and only highlight the "new" part of the diff, which should be fairly easy. Looking at delta's output it actually seems to do the same thing. To highlight the deleted lines I'd have to reproduce the old version of the file first, which can be implemented at a later point if it's needed.

@Piturnah
Copy link
Owner

Piturnah commented Oct 2, 2023

Thanks for the first review, appreciate it! I'll probably take my time with this PR.

Absolutely take your time. Thanks again for taking a look at this!

One problem I didn't notice until now is that highlighting each hunk in isolation is wrong, since it doesn't always produce valid syntax.
So I'll have to perform highlighting to the whole file first and then take the highlighted lines for each hunk from there. I'll probably cut some corners for now and only highlight the "new" part of the diff, which should be fairly easy. Looking at delta's output it actually seems to do the same thing. To highlight the deleted lines I'd have to reproduce the old version of the file first, which can be implemented at a later point if it's needed.

Seems good to me!

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.

2 participants