summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDan Davison <dandavison7@gmail.com>2021-11-21 18:23:35 -0500
committerDan Davison <dandavison7@gmail.com>2021-11-22 13:18:15 -0500
commitd000e40e61871d2bf8609a0db1beb71f48228147 (patch)
tree0542ea14dcf998486e79993e7a88df6997a0207b
parent07892bc572a92bca8e3fec3d1b0ba717d80cc096 (diff)
Syntax-highlight `git show $revision:./path/to/file.ext` output
-rw-r--r--src/delta.rs2
-rw-r--r--src/handlers/git_show_file.rs33
-rw-r--r--src/handlers/mod.rs1
-rw-r--r--src/paint.rs1
-rw-r--r--src/utils/process.rs58
5 files changed, 88 insertions, 7 deletions
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<String>), // 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<bool> {
+ 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<String>, HashSet<String>)),
- OtherGrep, // rg, grep, ag, ack, etc
+ GitShow(String), // (extension)
+ GitGrep((HashSet<String>, HashSet<String>)), // ((long_options, short_options))
+ OtherGrep, // rg, grep, ag, ack, etc
}
pub fn calling_process() -> Option<Cow<'static, CallingProcess>> {
@@ -79,14 +80,23 @@ pub fn describe_calling_process(args: &[String]) -> ProcessArgs<CallingProcess>
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<CallingProcess>
}
}
+fn get_git_show_file_extension<'a>(args: impl Iterator<Item = &'a str>) -> 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.
@@ -716,6 +738,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.
if std::env::vars().any(|(key, _)| key == "CROSS_RUNNER" || key == "QEMU_LD_PREFIX") {