-
Notifications
You must be signed in to change notification settings - Fork 16
/
Copy pathblame_all.py
178 lines (136 loc) · 6.84 KB
/
blame_all.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
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
import sublime
import sublime_plugin
from .base import BaseBlame
from .templates import blame_all_phantom_css, blame_all_phantom_html_template
from .settings import pkg_settings
from .settings import PKG_SETTINGS_BLAME_ALL_MESSAGE_MAX_LEN
from .settings import PKG_SETTINGS_BLAME_ALL_DISPLAY_AUTHOR
from .settings import PKG_SETTINGS_BLAME_ALL_DISPLAY_DATE
from .settings import PKG_SETTINGS_BLAME_ALL_DISPLAY_TIME
VIEW_SETTINGS_KEY_PHANTOM_ALL_DISPLAYED = "git-blame-all-displayed"
VIEW_SETTINGS_KEY_RULERS = "rulers" # A stock ST setting
VIEW_SETTINGS_KEY_RULERS_PREV = "rulers_prev" # Made up by us
class BlameShowAll(BaseBlame, sublime_plugin.TextCommand):
HORIZONTAL_SCROLL_DELAY_MS = 100
# Overrides (TextCommand) ----------------------------------------------------------
def __init__(self, view):
super().__init__(view)
self.phantom_set = sublime.PhantomSet(self.view, self.phantom_set_key())
self.pattern = None
def run(self, edit):
if not self.has_suitable_view():
self.tell_user_to_save()
return
self.view.erase_phantoms(self.phantom_set_key())
phantoms = [] # type: list[sublime.Phantom] # type: ignore[misc]
# If they are currently shown, toggle them off and return.
if self.view.settings().get(VIEW_SETTINGS_KEY_PHANTOM_ALL_DISPLAYED, False):
self.phantom_set.update(phantoms)
self.view.settings().erase(VIEW_SETTINGS_KEY_PHANTOM_ALL_DISPLAYED)
self.view.run_command("blame_restore_rulers")
# Workaround a visible empty space sometimes remaining in the viewport.
self.horizontal_scroll_to_limit(left=False)
self.horizontal_scroll_to_limit(left=True)
return
try:
blame_output = self.get_blame_text(self.view.file_name())
except Exception as e:
self.communicate_error(e)
return
blames = [self.parse_line(line) for line in blame_output.splitlines()]
blames = [b for b in blames if b]
if not blames:
self.communicate_error(
"Failed to parse anything for {0}. Has git's output format changed?".format(
self.__class__.__name__
)
)
return
max_author_len = max(len(b["author"]) for b in blames)
for blame in blames:
line_number = int(blame["line_number"])
author = blame["author"]
message_len = pkg_settings().get(PKG_SETTINGS_BLAME_ALL_MESSAGE_MAX_LEN)
message = ""
if message_len > 0:
message = self.get_commit_message_subject(blame["sha"].strip('^'), self.view.file_name())
message.strip()
if len(message) > message_len: message = message[0:message_len].strip()
message = message + ' ' * (message_len - len(message))
message = " " + message
display_author = pkg_settings().get(PKG_SETTINGS_BLAME_ALL_DISPLAY_AUTHOR)
display_date = pkg_settings().get(PKG_SETTINGS_BLAME_ALL_DISPLAY_DATE)
display_time = pkg_settings().get(PKG_SETTINGS_BLAME_ALL_DISPLAY_TIME)
sha = blame["sha"]
if sha == '00000000': sha = " " * 8
else: sha = '<a href="show?sha={sha}">{sha}</a>'.format(sha=sha)
phantom = sublime.Phantom(
self.phantom_region(line_number),
blame_all_phantom_html_template.format(
css=blame_all_phantom_css,
sha=sha,
message=message,
author=" " + author + " " * (max_author_len - len(author)) if display_author else "",
date=" " + blame["date"] if display_date else "",
time=" " + blame["time"] if display_time else "",
),
sublime.LAYOUT_INLINE,
self.handle_phantom_button,
)
phantoms.append(phantom)
self.phantom_set.update(phantoms)
self.view.settings().set(VIEW_SETTINGS_KEY_PHANTOM_ALL_DISPLAYED, True)
self.store_rulers()
# Bring the phantoms into view without the user needing to manually scroll left.
self.horizontal_scroll_to_limit(left=True)
# Overrides (BaseBlame) ------------------------------------------------------------
def _view(self):
return self.view
def extra_cli_args(self, **kwargs):
return []
def close_by_user_request(self):
self.view.run_command("blame_erase_all")
def rerun(self, **kwargs):
self.run(None)
# Overrides end --------------------------------------------------------------------
def phantom_region(self, line_number):
line_begins_pt = self.view.text_point(line_number - 1, 0)
return sublime.Region(line_begins_pt)
def store_rulers(self):
self.view.settings().set(
VIEW_SETTINGS_KEY_RULERS_PREV,
self.view.settings().get(VIEW_SETTINGS_KEY_RULERS),
)
self.view.settings().set(VIEW_SETTINGS_KEY_RULERS, [])
def horizontal_scroll_to_limit(self, *, left):
x = 0.0 if left else self.view.layout_extent()[0]
y = self.view.viewport_position()[1]
# NOTE: The scrolling doesn't seem to work if called inline (or with a 0ms timeout).
sublime.set_timeout(
lambda: self.view.set_viewport_position((x, y)),
self.HORIZONTAL_SCROLL_DELAY_MS,
)
class BlameEraseAll(sublime_plugin.TextCommand):
# Overrides begin ------------------------------------------------------------------
def run(self, edit):
sublime.status_message("The git blame result is cleared.")
self.view.erase_phantoms(BlameShowAll.phantom_set_key())
self.view.settings().erase(VIEW_SETTINGS_KEY_PHANTOM_ALL_DISPLAYED)
self.view.run_command("blame_restore_rulers")
# Overrides end --------------------------------------------------------------------
class BlameEraseAllListener(sublime_plugin.ViewEventListener):
# Overrides begin ------------------------------------------------------------------
@classmethod
def is_applicable(cls, settings):
return settings.get(VIEW_SETTINGS_KEY_PHANTOM_ALL_DISPLAYED, False)
def on_modified_async(self):
self.view.run_command("blame_erase_all")
# Overrides end --------------------------------------------------------------------
class BlameRestoreRulers(sublime_plugin.TextCommand):
# Overrides begin ------------------------------------------------------------------
def run(self, edit):
self.view.settings().set(
VIEW_SETTINGS_KEY_RULERS,
self.view.settings().get(VIEW_SETTINGS_KEY_RULERS_PREV),
)
# Overrides end --------------------------------------------------------------------