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', '') |