-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathgit_gutter_show_diff.py
200 lines (169 loc) · 7.3 KB
/
git_gutter_show_diff.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
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
import os
import sublime
try:
from .git_gutter_settings import settings
from .promise import Promise
except (ImportError, ValueError):
from git_gutter_settings import settings
from promise import Promise
ST3 = int(sublime.version()) >= 3000
class GitGutterShowDiff(object):
region_names = ['deleted_top', 'deleted_bottom',
'deleted_dual', 'inserted', 'changed',
'untracked', 'ignored']
def __init__(self, view, git_handler):
self.view = view
self.git_handler = git_handler
self.diff_results = None
def run(self):
self.git_handler.diff().then(self._check_ignored_or_untracked)
def _check_ignored_or_untracked(self, contents):
show_untracked = settings.get(
'show_markers_on_untracked_file', False)
if show_untracked and self._are_all_lines_added(contents):
def bind_ignored_or_untracked(is_ignored):
if is_ignored:
self._bind_files('ignored')
else:
def bind_untracked(is_untracked):
if is_untracked:
self._bind_files('untracked')
else:
self._lazy_update_ui(contents)
self.git_handler.untracked().then(bind_untracked)
self.git_handler.ignored().then(bind_ignored_or_untracked)
return
self._lazy_update_ui(contents)
# heuristic to determine if the file is either untracked or ignored: all
# lines show up as "inserted" in the diff. Relying on the output of the
# normal diff command to trigger the actual untracked / ignored check (which
# is expensive because it's two separate git ls-files calls) allows us to
# save the extra git calls
def _are_all_lines_added(self, contents):
inserted, modified, deleted = contents
if len(modified) == 0 and len(deleted) == 0:
chars = self.view.size()
region = sublime.Region(0, chars)
return len(self.view.split_by_newlines(region)) == len(inserted)
else:
return False
def _lazy_update_ui(self, contents):
if self.diff_results is None or self.diff_results != contents:
self.diff_results = contents
self._update_ui(contents)
def _update_ui(self, contents):
inserted, modified, deleted = contents
self._clear_all()
self._lines_removed(deleted)
self._bind_icons('inserted', inserted)
self._bind_icons('changed', modified)
if settings.show_status != "none":
if settings.show_status == 'all':
def decode_and_strip(branch_name):
return branch_name.decode("utf-8").strip()
branch_promise = self.git_handler.git_current_branch().then(
decode_and_strip)
else:
branch_promise = Promise.resolve("")
def update_status_ui(branch_name):
self._update_status(
len(inserted), len(modified), len(deleted),
settings.get_compare_against(self.view), branch_name)
branch_promise.then(update_status_ui)
else:
self._update_status(0, 0, 0, "", "")
def _update_status(self, inserted, modified, deleted, compare, branch):
def set_status_if(test, key, message):
if test:
self.view.set_status("git_gutter_status_" + key, message)
else:
self.view.set_status("git_gutter_status_" + key, "")
# set_status_if(inserted > 0, "inserted", "Inserted : %d" % inserted)
# set_status_if(modified > 0, "modified", "Modified : %d" % modified)
# set_status_if(deleted > 0, "deleted", "Deleted : %d regions" % deleted)
# set_status_if(compare, "comparison", "Comparing against : %s" % compare)
# set_status_if(branch, "branch", "On branch : %s" % branch)
# [master - head, +0, -0, ~0]
if branch:
git_gutter_status = "[%s - %s, +%s, -%s, ■%s]" % (branch, compare, inserted, deleted, modified)
else:
git_gutter_status = ""
self.view.set_status("git_gutter_status", git_gutter_status)
def _clear_all(self):
for region_name in self.region_names:
self.view.erase_regions('git_gutter_%s' % region_name)
def _is_region_protected(self, region):
# Load protected Regions from Settings
protected_regions = settings.get('protected_regions', [])
# List of Lists of Regions
sets = [self.view.get_regions(r) for r in protected_regions]
# List of Regions
regions = [r for rs in sets for r in rs]
# get the line of the region (gutter icon applies to whole line)
region_line = self.view.line(region)
for r in regions:
if r.contains(region) or region_line.contains(r):
return True
return False
def _lines_to_regions(self, lines):
regions = []
for line in lines:
position = self.view.text_point(line - 1, 0)
region = sublime.Region(position, position + 1)
if not self._is_region_protected(region):
regions.append(region)
return regions
def _lines_removed(self, lines):
top_lines = lines
bottom_lines = [line - 1 for line in lines if line > 1]
dual_lines = []
for line in top_lines:
if line in bottom_lines:
dual_lines.append(line)
for line in dual_lines:
bottom_lines.remove(line)
top_lines.remove(line)
self._bind_icons('deleted_top', top_lines)
self._bind_icons('deleted_bottom', bottom_lines)
self._bind_icons('deleted_dual', dual_lines)
def _plugin_dir(self):
path = os.path.realpath(__file__)
root = os.path.split(os.path.dirname(path))[1]
return os.path.splitext(root)[0]
def _icon_path(self, icon_name):
if icon_name in ['deleted_top', 'deleted_bottom', 'deleted_dual']:
if self.view.line_height() > 15:
icon_name = icon_name + "_arrow"
if int(sublime.version()) < 3014:
path = '../GitGutter'
extn = ''
else:
path = 'Packages/' + self._plugin_dir()
extn = '.png'
return "/".join([path, 'icons', icon_name + extn])
def _bind_icons(self, event, lines):
regions = self._lines_to_regions(lines)
event_scope = event
if event.startswith('deleted'):
event_scope = 'deleted'
scope = 'markup.%s.git_gutter' % event_scope
icon = self._icon_path(event)
if ST3 and settings.show_in_minimap:
flags = sublime.DRAW_NO_FILL | sublime.DRAW_NO_OUTLINE
else:
flags = sublime.HIDDEN
self.view.add_regions(
'git_gutter_%s' % event, regions, scope, icon, flags)
def _bind_files(self, event):
lines = []
line_count = self._total_lines()
i = 0
while i < line_count:
lines += [i + 1]
i = i + 1
self._bind_icons(event, lines)
def _total_lines(self):
chars = self.view.size()
region = sublime.Region(0, chars)
lines = self.view.lines(region)
return len(lines)