forked from jisaacks/GitGutter
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathgit_gutter_handler.py
140 lines (125 loc) · 4.61 KB
/
git_gutter_handler.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
import git_helper
import os
import sublime
import subprocess
import re
from view_collection import ViewCollection
class GitGutterHandler:
def __init__(self, view):
self.view = view
self.git_temp_file = ViewCollection.git_tmp_file(self.view)
self.buf_temp_file = ViewCollection.buf_tmp_file(self.view)
if self.on_disk():
self.git_tree = git_helper.git_tree(self.view)
self.git_dir = git_helper.git_dir(self.git_tree)
self.git_path = git_helper.git_file_path(self.view, self.git_tree)
def on_disk(self):
# if the view is saved to disk
return self.view.file_name() is not None
def reset(self):
if self.on_disk() and self.git_path:
self.view.window().run_command('git_gutter')
def get_git_path(self):
return self.git_path
def update_buf_file(self):
chars = self.view.size()
region = sublime.Region(0, chars)
# get encoding and clean it for python ex: "Western (ISO 8859-1)"
pattern = re.compile(r'.+\((.*)\)')
encoding = self.view.encoding()
if pattern.match(encoding):
encoding = pattern.sub(r'\1', self.view.encoding())
# Try conversion
try:
contents = self.view.substr(region).encode(encoding.replace(' ', ''))
except UnicodeError:
# Fallback to utf8-encoding
contents = self.view.substr(region).encode('utf-8')
contents = contents.replace('\r\n', '\n')
contents = contents.replace('\r', '\n')
f = open(self.buf_temp_file.name, 'w')
f.write(contents)
f.close()
def update_git_file(self):
# the git repo won't change that often
# so we can easily wait 5 seconds
# between updates for performance
if ViewCollection.git_time(self.view) > 5:
open(self.git_temp_file.name, 'w').close()
args = [
'git',
'--git-dir=' + self.git_dir,
'--work-tree=' + self.git_tree,
'show',
'HEAD:' + self.git_path,
]
try:
contents = self.run_command(args)
contents = contents.replace('\r\n', '\n')
contents = contents.replace('\r', '\n')
f = open(self.git_temp_file.name, 'w')
f.write(contents)
f.close()
ViewCollection.update_git_time(self.view)
except Exception:
pass
def total_lines(self):
chars = self.view.size()
region = sublime.Region(0, chars)
lines = self.view.lines(region)
lines_count = len(lines)
return range(1, lines_count + 1)
def process_diff(self, diff_str):
inserted = []
modified = []
deleted = []
pattern = re.compile(r'(\d+),?(\d*)(.)(\d+),?(\d*)')
lines = diff_str.splitlines()
for line in lines:
m = pattern.match(line)
if not m:
continue
kind = m.group(3)
line_start = int(m.group(4))
if len(m.group(5)) > 0:
line_end = int(m.group(5))
else:
line_end = line_start
if kind == 'c':
modified += range(line_start, line_end + 1)
elif kind == 'a':
inserted += range(line_start, line_end + 1)
elif kind == 'd':
if line == 1:
deleted.append(line_start)
else:
deleted.append(line_start + 1)
if inserted == self.total_lines():
# All lines are "inserted"
# this means this file is either:
# - New and not being tracked *yet*
# - Or it is a *gitignored* file
return ([], [], [])
else:
return (inserted, modified, deleted)
def diff(self):
if self.on_disk() and self.git_path:
self.update_git_file()
self.update_buf_file()
args = [
'diff',
self.git_temp_file.name,
self.buf_temp_file.name,
]
results = self.run_command(args)
return self.process_diff(results)
else:
return ([], [], [])
def run_command(self, args):
startupinfo = None
if os.name == 'nt':
startupinfo = subprocess.STARTUPINFO()
startupinfo.dwFlags |= subprocess.STARTF_USESHOWWINDOW
proc = subprocess.Popen(args, stdout=subprocess.PIPE,
startupinfo=startupinfo)
return proc.stdout.read()