-
Notifications
You must be signed in to change notification settings - Fork 0
/
blame.py
145 lines (115 loc) · 5.34 KB
/
blame.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
from urllib.parse import quote_plus
import sublime
import sublime_plugin
from .base import BaseBlame
from .templates import blame_phantom_css, blame_phantom_html_template
class Blame(BaseBlame, sublime_plugin.TextCommand):
# Overrides (TextCommand) ----------------------------------------------------------
def __init__(self, view):
super().__init__(view)
self.phantom_set = sublime.PhantomSet(view, self.phantom_set_key())
def run(self, edit, prevving=False, fixed_row_num=None, sha_skip_list=[]):
if not self.has_suitable_view():
self.tell_user_to_save()
return
phantoms = []
if prevving:
# We'll be getting blame information for the line whose existing phantom's
# [Prev] button was clicked, regardless of where the text cursor(s)
# currently are.
relevant_regions = [sublime.Region(self.view.text_point(fixed_row_num, 0))]
else:
# We'll be getting blame information for the lines where text cursor(s)
# currently are.
relevant_regions = self.view.sel()
for region in relevant_regions:
line_region = self.view.line(region)
# When this Command is ran for a line with a phantom already visible, we
# erase the phantom (i.e. toggle it). But if the reason this Command is
# being ran is because the user is clicking the [Prev] button, just erasing
# the existing phantom is not sufficient, because we need to then display
# another phantom with updated content.
if self.phantom_exists_for_region(line_region) and not prevving:
continue
row_num, _ = self.view.rowcol(region.begin())
line_num = row_num + 1
full_path = self.view.file_name()
try:
blame_output = self.get_blame_text(
full_path, line_num=line_num, sha_skip_list=sha_skip_list
)
except Exception as e:
self.communicate_error(e)
return
blame = self.parse_line(blame_output)
if not blame:
self.communicate_error(
"Failed to parse anything for {0}. Has git's output format changed?".format(
self.__class__.__name__
)
)
return
sha = blame["sha"]
sha_normalised = blame["sha_normalised"]
author = blame["author"]
date = blame["date"]
time = blame["time"]
if sha_skip_list:
recently_skipped_sha = sha_skip_list[-1]
if sha_normalised == recently_skipped_sha:
sublime.message_dialog(
"No earlier commits affected line {0}".format(line_num)
)
return
phantoms.append(
sublime.Phantom(
line_region,
blame_phantom_html_template.format(
css=blame_phantom_css,
sha=sha,
sha_not_latest_indicator=" *" if sha_skip_list else "",
author=author,
date=date,
time=time,
qs_row_num_val=quote_plus(str(row_num)),
qs_sha_val=quote_plus(sha_normalised),
# Querystrings can contain the same key multiple times. We use that
# functionality to accumulate a list of SHAs to skip over when
# a [Prev] button has been clicked multiple times.
qs_skip_keyvals="&".join(
[
"skip={0}".format(quote_plus(skipee))
for skipee in sha_skip_list
]
),
),
sublime.LAYOUT_BLOCK,
self.handle_phantom_button,
)
)
self.phantom_set.update(phantoms)
# Overrides (BaseBlame) ------------------------------------------------------------
def _view(self):
return self.view
def close_by_user_request(self):
self.phantom_set.update([])
def extra_cli_args(self, line_num, sha_skip_list):
args = ["-L", "{0},{0}".format(line_num)]
for skipped_sha in sha_skip_list:
args.extend(["--ignore-rev", skipped_sha])
return args
def rerun(self, **kwargs):
self.run(None, **kwargs)
# Overrides end --------------------------------------------------------------------
def phantom_exists_for_region(self, region):
return any(p.region == region for p in self.phantom_set.phantoms)
class BlameInsertCommitDescription(sublime_plugin.TextCommand):
# Overrides begin ------------------------------------------------------------------
def run(self, edit, desc, scratch_view_name):
view = self.view
view.set_scratch(True)
view.assign_syntax("Packages/Git Formats/Git Log.sublime-syntax")
view.insert(edit, 0, desc)
view.set_name(scratch_view_name)
view.set_read_only(True)
# Overrides end --------------------------------------------------------------------