170 lines
4.8 KiB
Plaintext
170 lines
4.8 KiB
Plaintext
import sublime
|
|
import sublime_plugin
|
|
import functools
|
|
import threading
|
|
import subprocess
|
|
import sys
|
|
import re
|
|
import tempfile
|
|
import codecs
|
|
import os
|
|
import time
|
|
|
|
from SideBarGit import SideBarGit
|
|
from SideBarItem import SideBarItem
|
|
from Utils import Object
|
|
|
|
Object.running = False
|
|
Object.timing = time.time()
|
|
|
|
class SideBarGitGutterDiff(sublime_plugin.EventListener):
|
|
|
|
def on_load(self, view):
|
|
self.run(view)
|
|
|
|
def on_modified(self, view):
|
|
now = time.time()
|
|
if now - Object.timing > 0.1:
|
|
Object.timing = now
|
|
self.run(view)
|
|
else:
|
|
Object.timing = now
|
|
|
|
def on_post_save(self, view):
|
|
self.run(view)
|
|
|
|
def run(self, view):
|
|
if Object.running == False and view.file_name() != None and view.file_name() != '':
|
|
Object.running = True
|
|
# cache file repository ( if any )
|
|
if not view.settings().has('SideBarGitGutterRepository'):
|
|
item = SideBarItem(view.file_name(), False)
|
|
_item = SideBarItem(view.file_name(), False)
|
|
repos = SideBarGit().getSelectedRepos([_item])
|
|
if len(repos) > 0:
|
|
view.settings().set('SideBarGitGutterRepository', repos[0].repository.path())
|
|
view.settings().set('SideBarGitGutterCWD', repos[0].repository.path())
|
|
view.settings().set('SideBarGitGutterPath', item.forCwdSystemPathRelativeFrom(repos[0].repository.path()))
|
|
else:
|
|
view.settings().set('SideBarGitGutterRepository', '')
|
|
|
|
# if in a repo check for modifications
|
|
repo = view.settings().get('SideBarGitGutterRepository')
|
|
if repo != '':
|
|
SideBarGitGutterDiffThread(
|
|
view,
|
|
repo,
|
|
view.settings().get('SideBarGitGutterCWD'),
|
|
view.settings().get('SideBarGitGutterPath'),
|
|
view.substr(sublime.Region(0, view.size()))
|
|
).start()
|
|
else:
|
|
Object.running = False
|
|
|
|
class SideBarGitGutterDiffThread(threading.Thread):
|
|
|
|
def __init__(self, view, repo, cwd, path, content):
|
|
threading.Thread.__init__(self)
|
|
self.view = view
|
|
self.repo = repo
|
|
self.cwd = cwd
|
|
self.path = path
|
|
self.content = content
|
|
|
|
def run(self):
|
|
|
|
tmp = tempfile.NamedTemporaryFile(delete=False)
|
|
codecs.open(tmp.name, 'w+', 'utf-8').write(self.content)
|
|
|
|
comand = ['git', 'diff', '-p', '--unified=0', '--no-color', '--ignore-all-space', '--ignore-space-at-eol', '--ignore-space-change', 'HEAD:'+self.path, tmp.name]
|
|
|
|
process = subprocess.Popen(
|
|
comand,
|
|
cwd=self.cwd,
|
|
stdout=subprocess.PIPE,
|
|
stderr=subprocess.STDOUT,
|
|
shell=sys.platform == 'win32',
|
|
universal_newlines=True)
|
|
|
|
stdout, stderr = process.communicate()
|
|
if stdout != '' and stdout.find('fatal:') != 0:
|
|
|
|
hunk = re.finditer('\n@@ -([0-9]+),?([0-9]*) \+([0-9]+),?([0-9]*) @@', stdout)
|
|
|
|
additions = []
|
|
deletions = []
|
|
changes = []
|
|
|
|
for change in hunk:
|
|
g = []
|
|
for group in change.groups():
|
|
if group == '':
|
|
g.append(1)
|
|
else:
|
|
g.append(int(group))
|
|
deleted = g[1]
|
|
added = g[3]
|
|
|
|
if deleted == added and added == 1:
|
|
changes.append([g[2]-1, g[2]]);
|
|
else:
|
|
if deleted > 0:
|
|
if deleted == 1:
|
|
if added > deleted:
|
|
deletions.append([g[2], g[2]+deleted-added]);
|
|
else:
|
|
deletions.append([g[2], g[2]+1]);
|
|
else:
|
|
deletions.append([g[2]-1, g[2]+deleted-1])
|
|
if added > 0:
|
|
if added == 1:
|
|
additions.append([g[2], g[2]+added]);
|
|
else:
|
|
additions.append([g[2]-1, g[2]+added-1])
|
|
tmp.close();
|
|
os.remove(tmp.name)
|
|
sublime.set_timeout(functools.partial(self.add_regions, additions, deletions, changes), 0)
|
|
else:
|
|
tmp.close();
|
|
os.remove(tmp.name)
|
|
sublime.set_timeout(functools.partial(self.erase_regions), 0)
|
|
if stdout.find('fatal:'):
|
|
sublime.set_timeout(functools.partial(self.mark_not_in_a_repository), 0)
|
|
Object.running = False
|
|
|
|
def add_regions(self, additions, deletions, changes):
|
|
|
|
self.erase_regions()
|
|
|
|
rs = []
|
|
for r in additions:
|
|
while r[0] != r[1]:
|
|
rs.append(sublime.Region(self.view.text_point(r[0], 0)))
|
|
r[0] = r[0]+1
|
|
if len(rs):
|
|
self.view.add_regions("git.diff.additions", rs, "number", "dot", sublime.HIDDEN)
|
|
|
|
rs = []
|
|
for r in deletions:
|
|
while r[0] != r[1]:
|
|
rs.append(sublime.Region(self.view.text_point(r[0], 0)))
|
|
r[0] = r[0]+1
|
|
if len(rs):
|
|
self.view.add_regions("git.diff.deletions", rs, "entity.name.class", "dot", sublime.HIDDEN)
|
|
|
|
rs = []
|
|
for r in changes:
|
|
while r[0] != r[1]:
|
|
rs.append(sublime.Region(self.view.text_point(r[0], 0)))
|
|
r[0] = r[0]+1
|
|
if len(rs):
|
|
self.view.add_regions("git.diff.changes", rs, "string", "dot", sublime.HIDDEN)
|
|
|
|
|
|
def erase_regions(self):
|
|
self.view.erase_regions("git.diff.additions")
|
|
self.view.erase_regions("git.diff.deletions")
|
|
self.view.erase_regions("git.diff.changes")
|
|
|
|
def mark_not_in_a_repository(self):
|
|
self.view.settings().set('SideBarGitGutterRepository', '') |