Skip to content

Instantly share code, notes, and snippets.

@Tubbles
Forked from mplewis/LICENSE.md
Last active October 26, 2018 06:12
Show Gist options
  • Save Tubbles/ee045f98ce6b1205d26a3b33e70f642e to your computer and use it in GitHub Desktop.
Save Tubbles/ee045f98ce6b1205d26a3b33e70f642e to your computer and use it in GitHub Desktop.
git blame with colored authors and pygments syntax highlighting. Dependencies: `pip install pygments tabulate colored`
#!/usr/bin/env python3
color_groups = [
[('red', None), ('green', None), ('yellow', None), ('magenta', None),
('cyan', None)],
[('black', 'light_red'), ('black', 'light_green'),
('black', 'light_yellow'), ('black', 'light_magenta'),
('black', 'light_cyan')]
]
cmd = ['git', 'blame', '--line-porcelain']
###############################################
# START Base16 Tomorrow Dark style for Pygments
###############################################
# https://github.com/idleberg/base16-pygments/blob/master/base16-tomorrow.dark.py
from pygments.style import Style
from pygments.token import Keyword, Name, Comment, String, Error, Text, \
Number, Operator, Generic, Whitespace, Punctuation, Other, Literal
#BACKGROUND = "#1d1f21"
#CURRENT_LINE = "#282a2e"
#SELECTION = "#373b41"
#FOREGROUND = "#ffffff"
#COMMENT = "#969896"
#RED = "#cc6666"
#ORANGE = "#de935f"
#YELLOW = "#f0c674"
#GREEN = "#b5bd68"
#AQUA = "#8abeb7"
#BLUE = "#81a2be"
#PURPLE = "#b294bb"
# Solarized Dark
BACKGROUND = "#073642"
CURRENT_LINE = "#073642"
SELECTION = "#073642"
FOREGROUND = "#ccc6b3"
COMMENT = "#839496"
RED = "#dc322f"
ORANGE = "#b58900"
YELLOW = "#cb4b16"
GREEN = "#859900"
AQUA = "#d33682"
BLUE = "#268bd2"
PURPLE = "#6c71c4"
class base16_tomorrow_dark(Style):
default_style = ''
background_color = BACKGROUND
highlight_color = SELECTION
background_color = BACKGROUND
highlight_color = SELECTION
styles = {
# No corresponding class for the following:
Text: FOREGROUND, # class: ''
Whitespace: "", # class: 'w'
Error: RED, # class: 'err'
Other: "", # class 'x'
Comment: COMMENT, # class: 'c'
Comment.Multiline: "", # class: 'cm'
Comment.Preproc: "", # class: 'cp'
Comment.Single: "", # class: 'c1'
Comment.Special: "", # class: 'cs'
Keyword: PURPLE, # class: 'k'
Keyword.Constant: "", # class: 'kc'
Keyword.Declaration: "", # class: 'kd'
Keyword.Namespace: AQUA, # class: 'kn'
Keyword.Pseudo: "", # class: 'kp'
Keyword.Reserved: "", # class: 'kr'
Keyword.Type: YELLOW, # class: 'kt'
Operator: AQUA, # class: 'o'
Operator.Word: "", # class: 'ow' - like keywords
Punctuation: FOREGROUND, # class: 'p'
Name: FOREGROUND, # class: 'n'
Name.Attribute: BLUE, # class: 'na' - to be revised
Name.Builtin: "", # class: 'nb'
Name.Builtin.Pseudo: "", # class: 'bp'
Name.Class: YELLOW, # class: 'nc' - to be revised
Name.Constant: RED, # class: 'no' - to be revised
Name.Decorator: AQUA, # class: 'nd' - to be revised
Name.Entity: "", # class: 'ni'
Name.Exception: RED, # class: 'ne'
Name.Function: BLUE, # class: 'nf'
Name.Property: "", # class: 'py'
Name.Label: "", # class: 'nl'
Name.Namespace: YELLOW, # class: 'nn' - to be revised
Name.Other: BLUE, # class: 'nx'
Name.Tag: AQUA, # class: 'nt' - like a keyword
Name.Variable: RED, # class: 'nv' - to be revised
Name.Variable.Class: "", # class: 'vc' - to be revised
Name.Variable.Global: "", # class: 'vg' - to be revised
Name.Variable.Instance: "", # class: 'vi' - to be revised
Number: ORANGE, # class: 'm'
Number.Float: "", # class: 'mf'
Number.Hex: "", # class: 'mh'
Number.Integer: "", # class: 'mi'
Number.Integer.Long: "", # class: 'il'
Number.Oct: "", # class: 'mo'
Literal: ORANGE, # class: 'l'
Literal.Date: GREEN, # class: 'ld'
String: GREEN, # class: 's'
String.Backtick: "", # class: 'sb'
String.Char: FOREGROUND, # class: 'sc'
String.Doc: COMMENT, # class: 'sd' - like a comment
String.Double: "", # class: 's2'
String.Escape: ORANGE, # class: 'se'
String.Heredoc: "", # class: 'sh'
String.Interpol: ORANGE, # class: 'si'
String.Other: "", # class: 'sx'
String.Regex: "", # class: 'sr'
String.Single: "", # class: 's1'
String.Symbol: "", # class: 'ss'
Generic: "", # class: 'g'
Generic.Deleted: RED, # class: 'gd',
Generic.Emph: "italic", # class: 'ge'
Generic.Error: "", # class: 'gr'
Generic.Heading: "bold " + FOREGROUND, # class: 'gh'
Generic.Inserted: GREEN, # class: 'gi'
Generic.Output: "", # class: 'go'
Generic.Prompt: "bold " + COMMENT, # class: 'gp'
Generic.Strong: "bold", # class: 'gs'
Generic.Subheading: "bold " + AQUA, # class: 'gu'
Generic.Traceback: "", # class: 'gt'
}
#############################################
# END Base16 Tomorrow Dark style for Pygments
#############################################
from pygments import highlight
from pygments.lexers import guess_lexer
from pygments.formatters import Terminal256Formatter
from tabulate import tabulate
from colored import fg, bg, attr
from datetime import datetime
import subprocess
from sys import argv
import re
def lines_starting_with(all_lines, prefix):
grepped = [l for l in all_lines if l.startswith(prefix)]
stripped = [l.split(prefix)[1] for l in grepped]
return stripped
def lines_matching_regex(all_lines, regex):
pattern = re.compile(regex)
grepped = [l for l in all_lines if pattern.match(l)]
return grepped
def main():
cmd.extend(argv[1:])
gbl_raw = (subprocess.run(cmd, stdout=subprocess.PIPE).stdout.decode().split('\n'))
code = '\n'.join(lines_starting_with(gbl_raw, '\t'))
hash_by_line = lines_matching_regex(gbl_raw,'^[a-fA-F0-9]{40}')
authors_by_line = lines_starting_with(gbl_raw, 'author ')
authors_unique = sorted(list(set(authors_by_line)))
time_by_line = lines_starting_with(gbl_raw, 'author-time ')
tz_by_line = lines_starting_with(gbl_raw, 'author-tz ')
summary_by_line = lines_starting_with(gbl_raw, 'summary ')
formatter = Terminal256Formatter(style=base16_tomorrow_dark)
highlighted_raw = highlight(code, guess_lexer(code), formatter)
highlighted = highlighted_raw.split('\n')
color_codes = []
for group in color_groups:
for fg_color, bg_color in group:
color_code = fg(fg_color)
if bg_color:
color_code += bg(bg_color)
color_codes.append(color_code)
author_color_codes = {author: color_codes.pop(0) for author in authors_unique}
pretty_blame = []
for i in range(min(len(authors_by_line), len(highlighted))):
hash = hash_by_line[i]
author = authors_by_line[i]
time = datetime.utcfromtimestamp(int(time_by_line[i])).strftime('%Y-%m-%d %H:%M:%S')
tz = tz_by_line[i]
summary = summary_by_line[i]
pretty_blame.append((
fg('yellow') + hash[:7],
fg('blue') + time + ' ' + tz,
author_color_codes[author] + author[:10],
fg('yellow') + summary[:20],
fg('dark_gray') + str(i),
attr('reset') + highlighted[i]
))
print(tabulate(pretty_blame, tablefmt='plain'))
if __name__ == '__main__':
main()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment