feat(ST2.GitPackages): bump up all packages
- Refresh PackageCache with latest versions of everything
This commit is contained in:
@@ -0,0 +1,390 @@
|
||||
# python3-compatible git library from https://github.com/kemayo/sublime-text-2-git
|
||||
# including this temporarily until a Package Control installable version of Git
|
||||
# is available
|
||||
import os
|
||||
import sublime
|
||||
import sublime_plugin
|
||||
import threading
|
||||
import subprocess
|
||||
import functools
|
||||
import os.path
|
||||
import time
|
||||
|
||||
# In a complete inversion from ST2, in ST3 when a plugin is loaded we
|
||||
# actually can trust __file__.
|
||||
# Goal is to get: "Packages/Git", allowing for people who rename things
|
||||
FULL_PLUGIN_DIRECTORY = os.path.dirname(os.path.realpath(__file__))
|
||||
PLUGIN_DIRECTORY = FULL_PLUGIN_DIRECTORY.replace(os.path.normpath(os.path.join(FULL_PLUGIN_DIRECTORY, '..', '..')) + os.path.sep, '').replace(os.path.sep, '/')
|
||||
|
||||
git_root_cache = {}
|
||||
|
||||
|
||||
def main_thread(callback, *args, **kwargs):
|
||||
# sublime.set_timeout gets used to send things onto the main thread
|
||||
# most sublime.[something] calls need to be on the main thread
|
||||
sublime.set_timeout(functools.partial(callback, *args, **kwargs), 0)
|
||||
|
||||
|
||||
def open_url(url):
|
||||
sublime.active_window().run_command('open_url', {"url": url})
|
||||
|
||||
|
||||
def git_root(directory):
|
||||
global git_root_cache
|
||||
|
||||
retval = False
|
||||
leaf_dir = directory
|
||||
|
||||
if leaf_dir in git_root_cache and git_root_cache[leaf_dir]['expires'] > time.time():
|
||||
return git_root_cache[leaf_dir]['retval']
|
||||
|
||||
while directory:
|
||||
if os.path.exists(os.path.join(directory, '.git')):
|
||||
retval = directory
|
||||
break
|
||||
parent = os.path.realpath(os.path.join(directory, os.path.pardir))
|
||||
if parent == directory:
|
||||
# /.. == /
|
||||
retval = False
|
||||
break
|
||||
directory = parent
|
||||
|
||||
git_root_cache[leaf_dir] = {
|
||||
'retval': retval,
|
||||
'expires': time.time() + 5
|
||||
}
|
||||
|
||||
return retval
|
||||
|
||||
|
||||
# for readability code
|
||||
def git_root_exist(directory):
|
||||
return git_root(directory)
|
||||
|
||||
|
||||
def view_contents(view):
|
||||
region = sublime.Region(0, view.size())
|
||||
return view.substr(region)
|
||||
|
||||
|
||||
def plugin_file(name):
|
||||
return os.path.join(PLUGIN_DIRECTORY, name)
|
||||
|
||||
|
||||
def do_when(conditional, callback, *args, **kwargs):
|
||||
if conditional():
|
||||
return callback(*args, **kwargs)
|
||||
sublime.set_timeout(functools.partial(do_when, conditional, callback, *args, **kwargs), 50)
|
||||
|
||||
|
||||
def _make_text_safeish(text, fallback_encoding, method='decode'):
|
||||
# The unicode decode here is because sublime converts to unicode inside
|
||||
# insert in such a way that unknown characters will cause errors, which is
|
||||
# distinctly non-ideal... and there's no way to tell what's coming out of
|
||||
# git in output. So...
|
||||
try:
|
||||
unitext = getattr(text, method)('utf-8')
|
||||
except (UnicodeEncodeError, UnicodeDecodeError):
|
||||
unitext = getattr(text, method)(fallback_encoding)
|
||||
except AttributeError:
|
||||
# strongly implies we're already unicode, but just in case let's cast
|
||||
# to string
|
||||
unitext = str(text)
|
||||
return unitext
|
||||
|
||||
|
||||
def _test_paths_for_executable(paths, test_file):
|
||||
for directory in paths:
|
||||
file_path = os.path.join(directory, test_file)
|
||||
if os.path.exists(file_path) and os.access(file_path, os.X_OK):
|
||||
return file_path
|
||||
def find_git():
|
||||
# It turns out to be difficult to reliably run git, with varying paths
|
||||
# and subprocess environments across different platforms. So. Let's hack
|
||||
# this a bit.
|
||||
# (Yes, I could fall back on a hardline "set your system path properly"
|
||||
# attitude. But that involves a lot more arguing with people.)
|
||||
path = os.environ.get('PATH', '').split(os.pathsep)
|
||||
if os.name == 'nt':
|
||||
git_cmd = 'git.exe'
|
||||
else:
|
||||
git_cmd = 'git'
|
||||
|
||||
git_path = _test_paths_for_executable(path, git_cmd)
|
||||
|
||||
if not git_path:
|
||||
# /usr/local/bin:/usr/local/git/bin
|
||||
if os.name == 'nt':
|
||||
extra_paths = (
|
||||
os.path.join(os.environ["ProgramFiles"], "Git", "bin"),
|
||||
os.path.join(os.environ["ProgramFiles(x86)"], "Git", "bin"),
|
||||
)
|
||||
else:
|
||||
extra_paths = (
|
||||
'/usr/local/bin',
|
||||
'/usr/local/git/bin',
|
||||
)
|
||||
git_path = _test_paths_for_executable(extra_paths, git_cmd)
|
||||
return git_path
|
||||
GIT = find_git()
|
||||
|
||||
|
||||
class CommandThread(threading.Thread):
|
||||
def __init__(self, command, on_done, working_dir="", fallback_encoding="", **kwargs):
|
||||
threading.Thread.__init__(self)
|
||||
self.command = command
|
||||
self.on_done = on_done
|
||||
self.working_dir = working_dir
|
||||
if "stdin" in kwargs:
|
||||
self.stdin = kwargs["stdin"].encode()
|
||||
else:
|
||||
self.stdin = None
|
||||
if "stdout" in kwargs:
|
||||
self.stdout = kwargs["stdout"]
|
||||
else:
|
||||
self.stdout = subprocess.PIPE
|
||||
self.fallback_encoding = fallback_encoding
|
||||
self.kwargs = kwargs
|
||||
|
||||
def run(self):
|
||||
try:
|
||||
# Ignore directories that no longer exist
|
||||
if not os.path.isdir(self.working_dir):
|
||||
return
|
||||
|
||||
if self.working_dir != "":
|
||||
os.chdir(self.working_dir)
|
||||
|
||||
# Windows needs startupinfo in order to start process in background
|
||||
startupinfo = None
|
||||
if os.name == 'nt':
|
||||
startupinfo = subprocess.STARTUPINFO()
|
||||
startupinfo.dwFlags |= subprocess.STARTF_USESHOWWINDOW
|
||||
|
||||
# universal_newlines seems to break `log` in python3
|
||||
proc = subprocess.Popen(self.command,
|
||||
stdout=self.stdout, stderr=subprocess.STDOUT,
|
||||
stdin=subprocess.PIPE, startupinfo=startupinfo,
|
||||
shell=False, universal_newlines=False)
|
||||
output = proc.communicate(self.stdin)[0]
|
||||
if not output:
|
||||
output = ''
|
||||
|
||||
main_thread(self.on_done,
|
||||
_make_text_safeish(output, self.fallback_encoding), **self.kwargs)
|
||||
except subprocess.CalledProcessError as e:
|
||||
main_thread(self.on_done, e.returncode)
|
||||
except OSError as e:
|
||||
if e.errno == 2:
|
||||
main_thread(sublime.error_message, "Git binary could not be found in PATH\n\nConsider using the git_command setting for the Git plugin\n\nPATH is: %s" % os.environ['PATH'])
|
||||
else:
|
||||
raise e
|
||||
|
||||
|
||||
class GitScratchOutputCommand(sublime_plugin.TextCommand):
|
||||
def run(self, edit, output = '', output_file = None, clear = False):
|
||||
if clear:
|
||||
region = sublime.Region(0, self.view.size())
|
||||
self.view.erase(edit, region)
|
||||
self.view.insert(edit, 0, output)
|
||||
|
||||
|
||||
# A base for all commands
|
||||
class GitCommand(object):
|
||||
may_change_files = False
|
||||
|
||||
def run_command(self, command, callback=None, show_status=True,
|
||||
filter_empty_args=True, no_save=False, **kwargs):
|
||||
if filter_empty_args:
|
||||
command = [arg for arg in command if arg]
|
||||
if 'working_dir' not in kwargs:
|
||||
kwargs['working_dir'] = self.get_working_dir()
|
||||
if 'fallback_encoding' not in kwargs and self.active_view() and self.active_view().settings().get('fallback_encoding'):
|
||||
kwargs['fallback_encoding'] = self.active_view().settings().get('fallback_encoding').rpartition('(')[2].rpartition(')')[0]
|
||||
|
||||
s = sublime.load_settings("Git.sublime-settings")
|
||||
if s.get('save_first') and self.active_view() and self.active_view().is_dirty() and not no_save:
|
||||
self.active_view().run_command('save')
|
||||
if command[0] == 'git':
|
||||
if s.get('git_command'):
|
||||
command[0] = s.get('git_command')
|
||||
elif GIT:
|
||||
command[0] = GIT
|
||||
if command[0] == 'git-flow' and s.get('git_flow_command'):
|
||||
command[0] = s.get('git_flow_command')
|
||||
if not callback:
|
||||
callback = self.generic_done
|
||||
|
||||
thread = CommandThread(command, callback, **kwargs)
|
||||
thread.start()
|
||||
|
||||
if show_status:
|
||||
message = kwargs.get('status_message', False) or ' '.join(command)
|
||||
sublime.status_message(message)
|
||||
|
||||
def generic_done(self, result):
|
||||
if self.may_change_files and self.active_view() and self.active_view().file_name():
|
||||
if self.active_view().is_dirty():
|
||||
result = "WARNING: Current view is dirty.\n\n"
|
||||
else:
|
||||
# just asking the current file to be re-opened doesn't do anything
|
||||
print("reverting")
|
||||
position = self.active_view().viewport_position()
|
||||
self.active_view().run_command('revert')
|
||||
do_when(lambda: not self.active_view().is_loading(), lambda: self.active_view().set_viewport_position(position, False))
|
||||
# self.active_view().show(position)
|
||||
|
||||
view = self.active_view()
|
||||
if view and view.settings().get('live_git_annotations'):
|
||||
self.view.run_command('git_annotate')
|
||||
|
||||
if not result.strip():
|
||||
return
|
||||
self.panel(result)
|
||||
|
||||
def _output_to_view(self, output_file, output, clear=False,
|
||||
syntax="Packages/Diff/Diff.tmLanguage", **kwargs):
|
||||
output_file.set_syntax_file(syntax)
|
||||
args = {
|
||||
'output': output,
|
||||
'clear': clear
|
||||
}
|
||||
output_file.run_command('git_scratch_output', args)
|
||||
|
||||
def scratch(self, output, title=False, position=None, **kwargs):
|
||||
scratch_file = self.get_window().new_file()
|
||||
if title:
|
||||
scratch_file.set_name(title)
|
||||
scratch_file.set_scratch(True)
|
||||
self._output_to_view(scratch_file, output, **kwargs)
|
||||
scratch_file.set_read_only(True)
|
||||
if position:
|
||||
sublime.set_timeout(lambda: scratch_file.set_viewport_position(position), 0)
|
||||
return scratch_file
|
||||
|
||||
def panel(self, output, **kwargs):
|
||||
if not hasattr(self, 'output_view'):
|
||||
self.output_view = self.get_window().get_output_panel("git")
|
||||
self.output_view.set_read_only(False)
|
||||
self._output_to_view(self.output_view, output, clear=True, **kwargs)
|
||||
self.output_view.set_read_only(True)
|
||||
self.get_window().run_command("show_panel", {"panel": "output.git"})
|
||||
|
||||
def quick_panel(self, *args, **kwargs):
|
||||
self.get_window().show_quick_panel(*args, **kwargs)
|
||||
|
||||
|
||||
# A base for all git commands that work with the entire repository
|
||||
class GitWindowCommand(GitCommand, sublime_plugin.WindowCommand):
|
||||
def active_view(self):
|
||||
return self.window.active_view()
|
||||
|
||||
def _active_file_name(self):
|
||||
view = self.active_view()
|
||||
if view and view.file_name() and len(view.file_name()) > 0:
|
||||
return view.file_name()
|
||||
|
||||
@property
|
||||
def fallback_encoding(self):
|
||||
if self.active_view() and self.active_view().settings().get('fallback_encoding'):
|
||||
return self.active_view().settings().get('fallback_encoding').rpartition('(')[2].rpartition(')')[0]
|
||||
|
||||
# If there's no active view or the active view is not a file on the
|
||||
# filesystem (e.g. a search results view), we can infer the folder
|
||||
# that the user intends Git commands to run against when there's only
|
||||
# only one.
|
||||
def is_enabled(self):
|
||||
if self._active_file_name() or len(self.window.folders()) == 1:
|
||||
return bool(git_root(self.get_working_dir()))
|
||||
return False
|
||||
|
||||
def get_file_name(self):
|
||||
return ''
|
||||
|
||||
def get_relative_file_name(self):
|
||||
return ''
|
||||
|
||||
# If there is a file in the active view use that file's directory to
|
||||
# search for the Git root. Otherwise, use the only folder that is
|
||||
# open.
|
||||
def get_working_dir(self):
|
||||
file_name = self._active_file_name()
|
||||
if file_name:
|
||||
return os.path.realpath(os.path.dirname(file_name))
|
||||
else:
|
||||
try: # handle case with no open folder
|
||||
return self.window.folders()[0]
|
||||
except IndexError:
|
||||
return ''
|
||||
|
||||
def get_window(self):
|
||||
return self.window
|
||||
|
||||
|
||||
# A base for all git commands that work with the file in the active view
|
||||
class GitTextCommand(GitCommand, sublime_plugin.TextCommand):
|
||||
def active_view(self):
|
||||
return self.view
|
||||
|
||||
def is_enabled(self):
|
||||
# First, is this actually a file on the file system?
|
||||
if self.view.file_name() and len(self.view.file_name()) > 0:
|
||||
return bool(git_root(self.get_working_dir()))
|
||||
return False
|
||||
|
||||
def get_file_name(self):
|
||||
return os.path.basename(self.view.file_name())
|
||||
|
||||
def get_relative_file_name(self):
|
||||
working_dir = self.get_working_dir()
|
||||
file_path = working_dir.replace(git_root(working_dir), '')[1:]
|
||||
file_name = os.path.join(file_path, self.get_file_name())
|
||||
return file_name.replace('\\', '/') # windows issues
|
||||
|
||||
def get_working_dir(self):
|
||||
return os.path.realpath(os.path.dirname(self.view.file_name()))
|
||||
|
||||
def get_window(self):
|
||||
# Fun discovery: if you switch tabs while a command is working,
|
||||
# self.view.window() is None. (Admittedly this is a consequence
|
||||
# of my deciding to do async command processing... but, hey,
|
||||
# got to live with that now.)
|
||||
# I did try tracking the window used at the start of the command
|
||||
# and using it instead of view.window() later, but that results
|
||||
# panels on a non-visible window, which is especially useless in
|
||||
# the case of the quick panel.
|
||||
# So, this is not necessarily ideal, but it does work.
|
||||
return self.view.window() or sublime.active_window()
|
||||
|
||||
|
||||
# A few miscellaneous commands
|
||||
|
||||
|
||||
class GitCustomCommand(GitWindowCommand):
|
||||
may_change_files = True
|
||||
|
||||
def run(self):
|
||||
self.get_window().show_input_panel("Git command", "",
|
||||
self.on_input, None, None)
|
||||
|
||||
def on_input(self, command):
|
||||
command = str(command) # avoiding unicode
|
||||
if command.strip() == "":
|
||||
self.panel("No git command provided")
|
||||
return
|
||||
import shlex
|
||||
command_splitted = ['git'] + shlex.split(command)
|
||||
print(command_splitted)
|
||||
self.run_command(command_splitted)
|
||||
|
||||
|
||||
class GitGuiCommand(GitTextCommand):
|
||||
def run(self, edit):
|
||||
command = ['git', 'gui']
|
||||
self.run_command(command)
|
||||
|
||||
|
||||
class GitGitkCommand(GitTextCommand):
|
||||
def run(self, edit):
|
||||
command = ['gitk']
|
||||
self.run_command(command)
|
Reference in New Issue
Block a user