From d000e40e61871d2bf8609a0db1beb71f48228147 Mon Sep 17 00:00:00 2001 From: Dan Davison Date: Sun, 21 Nov 2021 18:23:35 -0500 Subject: Syntax-highlight `git show $revision:./path/to/file.ext` output --- src/delta.rs | 2 ++ src/handlers/git_show_file.rs | 33 ++++++++++++++++++++++++ src/handlers/mod.rs | 1 + src/paint.rs | 1 + src/utils/process.rs | 58 +++++++++++++++++++++++++++++++++++++------ 5 files changed, 88 insertions(+), 7 deletions(-) create mode 100644 src/handlers/git_show_file.rs diff --git a/src/delta.rs b/src/delta.rs index bfb7761f..fa983a69 100644 --- a/src/delta.rs +++ b/src/delta.rs @@ -23,6 +23,7 @@ pub enum State { SubmoduleLog, // In a submodule section, with gitconfig diff.submodule = log SubmoduleShort(String), // In a submodule section, with gitconfig diff.submodule = short Blame(String, Option), // In a line of `git blame` output (commit, repeat_blame_line). + GitShowFile, // In a line of `git show $revision:./path/to/file.ext` output Grep, // In a line of `git grep` output Unknown, // The following elements are created when a line is wrapped to display it: @@ -121,6 +122,7 @@ impl<'a> StateMachine<'a> { || self.handle_submodule_log_line()? || self.handle_submodule_short_line()? || self.handle_hunk_line()? + || self.handle_git_show_file_line()? || self.handle_blame_line()? || self.handle_grep_line()? || self.should_skip_line() diff --git a/src/handlers/git_show_file.rs b/src/handlers/git_show_file.rs new file mode 100644 index 00000000..2a4998ca --- /dev/null +++ b/src/handlers/git_show_file.rs @@ -0,0 +1,33 @@ +use crate::delta::{State, StateMachine}; +use crate::paint::{BgShouldFill, StyleSectionSpecifier}; +use crate::utils::process; + +impl<'a> StateMachine<'a> { + // If this is a line of `git show $revision:/path/to/file.ext` output then + // syntax-highlight it as language `ext`. + pub fn handle_git_show_file_line(&mut self) -> std::io::Result { + self.painter.emit()?; + let mut handled_line = false; + if matches!(self.state, State::Unknown) { + if let Some(process::CallingProcess::GitShow(extension)) = + process::calling_process().as_deref() + { + self.state = State::GitShowFile; + self.painter.set_syntax(Some(extension)); + self.painter.set_highlighter(); + } else { + return Ok(handled_line); + } + } + if matches!(self.state, State::GitShowFile) { + self.painter.syntax_highlight_and_paint_line( + &self.line, + StyleSectionSpecifier::Style(self.config.zero_style), + self.state.clone(), + BgShouldFill::default(), + ); + handled_line = true; + } + Ok(handled_line) + } +} diff --git a/src/handlers/mod.rs b/src/handlers/mod.rs index e9f9b268..c032a7b9 100644 --- a/src/handlers/mod.rs +++ b/src/handlers/mod.rs @@ -7,6 +7,7 @@ pub mod draw; pub mod file_meta; pub mod file_meta_diff; pub mod file_meta_misc; +pub mod git_show_file; pub mod grep; pub mod hunk; pub mod hunk_header; diff --git a/src/paint.rs b/src/paint.rs index 706dc5ce..529065f6 100644 --- a/src/paint.rs +++ b/src/paint.rs @@ -636,6 +636,7 @@ impl<'p> Painter<'p> { State::HunkHeader(_, _) => true, State::HunkMinus(Some(_)) | State::HunkPlus(Some(_)) => false, State::Blame(_, _) => true, + State::GitShowFile => true, State::Grep => true, State::Unknown | State::CommitMeta diff --git a/src/utils/process.rs b/src/utils/process.rs index 0000e9da..ee145f88 100644 --- a/src/utils/process.rs +++ b/src/utils/process.rs @@ -7,8 +7,9 @@ use lazy_static::lazy_static; #[derive(Clone, Debug, PartialEq)] pub enum CallingProcess { - GitGrep((HashSet, HashSet)), - OtherGrep, // rg, grep, ag, ack, etc + GitShow(String), // (extension) + GitGrep((HashSet, HashSet)), // ((long_options, short_options)) + OtherGrep, // rg, grep, ag, ack, etc } pub fn calling_process() -> Option> { @@ -79,14 +80,23 @@ pub fn describe_calling_process(args: &[String]) -> ProcessArgs match args.next() { Some(command) => match Path::new(command).file_stem() { Some(s) if s.to_str() == Some("git") => { - let mut args = args.skip_while(|s| *s != "grep"); + let mut args = args.skip_while(|s| *s != "grep" && *s != "show"); match args.next() { - Some(_) => { + Some("grep") => { ProcessArgs::Args(CallingProcess::GitGrep(parse_command_option_keys(args))) } - None => { - // It's git, but not git grep. Don't look at any - // more processes and return not-a-grep-command. + Some("show") => { + if let Some(extension) = get_git_show_file_extension(args) { + ProcessArgs::Args(CallingProcess::GitShow(extension.to_string())) + } else { + // It's git show, but we failed to determine the + // file extension. Don't look at any more processes. + ProcessArgs::ArgError + } + } + _ => { + // It's git, but not a subcommand that we parse. Don't + // look at any more processes. ProcessArgs::ArgError } } @@ -113,6 +123,18 @@ pub fn describe_calling_process(args: &[String]) -> ProcessArgs } } +fn get_git_show_file_extension<'a>(args: impl Iterator) -> Option<&'a str> { + if let Some(last_arg) = skip_uninteresting_args(args, "".split(' ')).last() { + // E.g. "HEAD~1:Makefile" or "775c3b8:./src/delta.rs" + match last_arg.split_once(':') { + Some((_, suffix)) => suffix.split('.').last(), + None => None, + } + } else { + None + } +} + // Skip all arguments starting with '-' from `args_it`. Also skip all arguments listed in // `skip_this_plus_parameter` plus their respective next argument. // Keep all arguments once a '--' is encountered. @@ -715,6 +737,28 @@ pub mod tests { ); } + #[test] + fn test_describe_calling_process_git_show() { + for (command, expected_extension) in [ + ("/usr/local/bin/git show 775c3b84:./src/hello.rs", "rs"), + ("/usr/local/bin/git show HEAD~1:Makefile", "Makefile"), + ( + "git -c x.y=z show --abbrev-commit 775c3b84:./src/hello.bye.R", + "R", + ), + ] { + let parent = MockProcInfo::with(&[ + (2, 100, "-shell", None), + (3, 100, command, Some(2)), + (4, 100, "delta", Some(3)), + ]); + assert_eq!( + calling_process_cmdline(parent, describe_calling_process), + Some(CallingProcess::GitShow(expected_extension.to_string())), + ); + } + } + #[test] fn test_process_calling_cmdline() { // Github runs CI tests for arm under qemu where where sysinfo can not find the parent process. -- cgit v1.2.3