feat(SublimeText2.GitPackages): cache packages
This commit is contained in:
3
EthanBrown.SublimeText2.GitPackages/tools/PackageCache/Modific/.gitignore
vendored
Normal file
3
EthanBrown.SublimeText2.GitPackages/tools/PackageCache/Modific/.gitignore
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
*.pyc
|
||||
images
|
||||
!.gitignore
|
@@ -0,0 +1,9 @@
|
||||
[
|
||||
{ "keys": ["ctrl+alt+c"], "command": "show_original_part" },
|
||||
{ "keys": ["ctrl+alt+r"], "command": "replace_modified_part" },
|
||||
{ "keys": ["ctrl+alt+d"], "command": "show_diff" },
|
||||
{ "keys": ["ctrl+alt+u"], "command": "uncommitted_files" },
|
||||
|
||||
{ "keys": ["ctrl+shift+pageup"], "command": "jump_between_changes", "args": {"direction": "prev"} },
|
||||
{ "keys": ["ctrl+shift+pagedown"], "command": "jump_between_changes", "args": {"direction": "next"} }
|
||||
]
|
@@ -0,0 +1,9 @@
|
||||
[
|
||||
{ "keys": ["ctrl+super+c"], "command": "show_original_part" },
|
||||
{ "keys": ["ctrl+super+r"], "command": "replace_modified_part" },
|
||||
{ "keys": ["ctrl+alt+d"], "command": "show_diff" },
|
||||
{ "keys": ["ctrl+super+u"], "command": "uncommitted_files" },
|
||||
|
||||
{ "keys": ["ctrl+shift+pageup"], "command": "jump_between_changes", "args": {"direction": "prev"} },
|
||||
{ "keys": ["ctrl+shift+pagedown"], "command": "jump_between_changes", "args": {"direction": "next"} }
|
||||
]
|
@@ -0,0 +1,9 @@
|
||||
[
|
||||
{ "keys": ["ctrl+alt+c"], "command": "show_original_part" },
|
||||
{ "keys": ["ctrl+alt+r"], "command": "replace_modified_part" },
|
||||
{ "keys": ["ctrl+alt+d"], "command": "show_diff" },
|
||||
{ "keys": ["ctrl+alt+u"], "command": "uncommitted_files" },
|
||||
|
||||
{ "keys": ["ctrl+shift+pageup"], "command": "jump_between_changes", "args": {"direction": "prev"} },
|
||||
{ "keys": ["ctrl+shift+pagedown"], "command": "jump_between_changes", "args": {"direction": "next"} }
|
||||
]
|
@@ -0,0 +1,90 @@
|
||||
[
|
||||
{
|
||||
"caption": "Preferences",
|
||||
"mnemonic": "n",
|
||||
"id": "preferences",
|
||||
"children":
|
||||
[
|
||||
{
|
||||
"caption": "Package Settings",
|
||||
"mnemonic": "P",
|
||||
"id": "package-settings",
|
||||
"children":
|
||||
[
|
||||
{
|
||||
"caption": "Modific",
|
||||
"children":
|
||||
[
|
||||
{
|
||||
"command": "open_file",
|
||||
"args": {"file": "${packages}/Modific/README.md"},
|
||||
"caption": "README"
|
||||
},
|
||||
{ "caption": "-" },
|
||||
{
|
||||
"command": "open_file",
|
||||
"args": {"file": "${packages}/Modific/Modific.sublime-settings"},
|
||||
"caption": "Settings – Default"
|
||||
},
|
||||
{
|
||||
"command": "open_file",
|
||||
"args": {"file": "${packages}/User/Modific.sublime-settings"},
|
||||
"caption": "Settings – User"
|
||||
},
|
||||
{ "caption": "-" },
|
||||
{
|
||||
"command": "open_file",
|
||||
"args": {
|
||||
"file": "${packages}/Modific/Default (OSX).sublime-keymap",
|
||||
"platform": "OSX"
|
||||
},
|
||||
"caption": "Key Bindings – Default"
|
||||
},
|
||||
{
|
||||
"command": "open_file",
|
||||
"args": {
|
||||
"file": "${packages}/Modific/Default (Linux).sublime-keymap",
|
||||
"platform": "Linux"
|
||||
},
|
||||
"caption": "Key Bindings – Default"
|
||||
},
|
||||
{
|
||||
"command": "open_file",
|
||||
"args": {
|
||||
"file": "${packages}/Modific/Default (Windows).sublime-keymap",
|
||||
"platform": "Windows"
|
||||
},
|
||||
"caption": "Key Bindings – Default"
|
||||
},
|
||||
{
|
||||
"command": "open_file",
|
||||
"args": {
|
||||
"file": "${packages}/User/Default (OSX).sublime-keymap",
|
||||
"platform": "OSX"
|
||||
},
|
||||
"caption": "Key Bindings – User"
|
||||
},
|
||||
{
|
||||
"command": "open_file",
|
||||
"args": {
|
||||
"file": "${packages}/User/Default (Linux).sublime-keymap",
|
||||
"platform": "Linux"
|
||||
},
|
||||
"caption": "Key Bindings – User"
|
||||
},
|
||||
{
|
||||
"command": "open_file",
|
||||
"args": {
|
||||
"file": "${packages}/User/Default (Windows).sublime-keymap",
|
||||
"platform": "Windows"
|
||||
},
|
||||
"caption": "Key Bindings – User"
|
||||
},
|
||||
{ "caption": "-" }
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
@@ -0,0 +1,618 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
import sublime
|
||||
import sublime_plugin
|
||||
import os
|
||||
import threading
|
||||
import subprocess
|
||||
import functools
|
||||
import re
|
||||
|
||||
|
||||
IS_ST3 = sublime.version().startswith('3')
|
||||
|
||||
def get_settings():
|
||||
return sublime.load_settings("Modific.sublime-settings")
|
||||
|
||||
|
||||
def get_vcs_settings():
|
||||
return get_settings().get('vcs', [
|
||||
["git", "git"],
|
||||
["svn", "svn"],
|
||||
["bzr", "bzr"],
|
||||
["hg", "hg"]
|
||||
])
|
||||
|
||||
|
||||
def vcs_root(directory):
|
||||
"""
|
||||
Determines root directory for VCS
|
||||
"""
|
||||
|
||||
vcs_check = [(lambda vcs: lambda dir: os.path.exists(os.path.join(dir, '.' + vcs))
|
||||
and {'root': dir, 'name': vcs})(vcs)
|
||||
for vcs, _ in get_vcs_settings()]
|
||||
|
||||
while directory:
|
||||
available = list(filter(lambda x: x, [check(directory) for check in vcs_check]))
|
||||
if available:
|
||||
return directory, available[0]
|
||||
|
||||
parent = os.path.realpath(os.path.join(directory, os.path.pardir))
|
||||
if parent == directory:
|
||||
# /.. == /
|
||||
return None, None
|
||||
directory = parent
|
||||
return None, None
|
||||
|
||||
|
||||
def get_vcs(directory):
|
||||
"""
|
||||
Determines, which of VCS systems we should use for given folder.
|
||||
Currently, uses priority of definitions in get_vcs_settings()
|
||||
"""
|
||||
root_dir, vcs = vcs_root(directory)
|
||||
return vcs
|
||||
|
||||
|
||||
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 _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 do_when(conditional, callback, *args, **kwargs):
|
||||
if conditional():
|
||||
return callback(*args, **kwargs)
|
||||
sublime.set_timeout(functools.partial(do_when, conditional, callback, *args, **kwargs), 50)
|
||||
|
||||
|
||||
class CommandThread(threading.Thread):
|
||||
def __init__(self, command, on_done, working_dir="", fallback_encoding="", console_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
|
||||
self.stdout = kwargs.get('stdout', subprocess.PIPE)
|
||||
self.console_encoding = console_encoding
|
||||
self.fallback_encoding = fallback_encoding
|
||||
self.kwargs = kwargs
|
||||
|
||||
def run(self):
|
||||
try:
|
||||
# Per http://bugs.python.org/issue8557 shell=True is required to
|
||||
# get $PATH on Windows. Yay portable code.
|
||||
shell = os.name == 'nt'
|
||||
if self.working_dir != "":
|
||||
os.chdir(self.working_dir)
|
||||
|
||||
if self.console_encoding:
|
||||
self.command = [s.encode(self.console_encoding) for s in self.command]
|
||||
|
||||
proc = subprocess.Popen(self.command,
|
||||
stdout=self.stdout, stderr=subprocess.STDOUT,
|
||||
stdin=subprocess.PIPE,
|
||||
shell=shell, universal_newlines=True)
|
||||
output = proc.communicate(self.stdin)[0]
|
||||
if not output:
|
||||
output = ''
|
||||
# if sublime's python gets bumped to 2.7 we can just do:
|
||||
# output = subprocess.check_output(self.command)
|
||||
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,
|
||||
"'%s' binary could not be found in PATH\n\nConsider using `vcs` property to specify PATH\n\nPATH is: %s" % (self.command[0], os.environ['PATH']))
|
||||
else:
|
||||
raise e
|
||||
|
||||
|
||||
class EditViewCommand(sublime_plugin.TextCommand):
|
||||
|
||||
def run(self, edit, command=None, output='', begin=0, region=None):
|
||||
"""
|
||||
For some reason Sublime's view.run_command() doesn't allow to pass tuples,
|
||||
therefore region must be a list
|
||||
"""
|
||||
region = sublime.Region(int(region[0]), int(region[1])) if region else None
|
||||
if command == 'insert':
|
||||
self.view.insert(edit, int(begin), output)
|
||||
elif command == 'replace':
|
||||
self.view.replace(edit, region, output)
|
||||
elif command == 'erase':
|
||||
self.view.erase(edit, region)
|
||||
else:
|
||||
print('Invalid command: ', command)
|
||||
raise
|
||||
|
||||
|
||||
class VcsCommand(object):
|
||||
may_change_files = False
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
self.settings = get_settings()
|
||||
super(VcsCommand, self).__init__(*args, **kwargs)
|
||||
|
||||
def run_command(self, command, callback=None, show_status=True,
|
||||
filter_empty_args=True, **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]
|
||||
kwargs['console_encoding'] = self.settings.get('console_encoding')
|
||||
|
||||
autosave = self.settings.get('autosave', True)
|
||||
if self.active_view() and self.active_view().is_dirty() and autosave:
|
||||
self.active_view().run_command('save')
|
||||
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))
|
||||
|
||||
if not result.strip():
|
||||
return
|
||||
self.panel(result)
|
||||
|
||||
def _output_to_view(self, output_file, output, clear=False,
|
||||
syntax="Packages/Diff/Diff.tmLanguage"):
|
||||
output_file.set_syntax_file(syntax)
|
||||
if clear:
|
||||
output_file.run_command('edit_view', dict(command='replace', region=[0, self.output_view.size()], output=output))
|
||||
else:
|
||||
output_file.run_command('edit_view', dict(command='insert', output=output))
|
||||
|
||||
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("vcs")
|
||||
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.vcs"})
|
||||
|
||||
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()
|
||||
|
||||
def active_view(self):
|
||||
return self.view
|
||||
|
||||
def get_window(self):
|
||||
if (hasattr(self, 'view') and hasattr(self.view, 'window')):
|
||||
return self.view.window()
|
||||
else:
|
||||
return sublime.active_window()
|
||||
|
||||
def get_working_dir(self):
|
||||
return os.path.dirname(self._active_file_name())
|
||||
|
||||
def is_enabled(self):
|
||||
file_name = self._active_file_name()
|
||||
if file_name and os.path.exists(file_name):
|
||||
return bool(get_vcs(self.get_working_dir()))
|
||||
return False
|
||||
|
||||
def get_user_command(self, vcs_name):
|
||||
return dict(get_vcs_settings()).get(vcs_name, False)
|
||||
|
||||
|
||||
class DiffCommand(VcsCommand):
|
||||
""" Here you can define diff commands for your VCS
|
||||
method name pattern: %(vcs_name)s_diff_command
|
||||
"""
|
||||
|
||||
def run(self, edit):
|
||||
vcs = get_vcs(self.get_working_dir())
|
||||
filepath = self.view.file_name()
|
||||
filename = os.path.basename(filepath)
|
||||
max_file_size = self.settings.get('max_file_size', 1024) * 1024
|
||||
if not os.path.exists(filepath) or os.path.getsize(filepath) > max_file_size:
|
||||
# skip large files
|
||||
return
|
||||
get_command = getattr(self, '{0}_diff_command'.format(vcs['name']), None)
|
||||
if get_command:
|
||||
self.run_command(get_command(filename), self.diff_done)
|
||||
|
||||
def diff_done(self, result):
|
||||
pass
|
||||
|
||||
def git_diff_command(self, file_name):
|
||||
return [self.get_user_command('git') or 'git', 'diff', '--no-color', '--no-ext-diff', '--', file_name]
|
||||
|
||||
def svn_diff_command(self, file_name):
|
||||
params = [self.get_user_command('svn') or 'svn', 'diff']
|
||||
if self.settings.get('svn_use_internal_diff', True):
|
||||
params.append('--internal-diff')
|
||||
if file_name.find('@') != -1:
|
||||
file_name += '@'
|
||||
params.extend(['--revision', 'HEAD'])
|
||||
params.extend([file_name])
|
||||
return params
|
||||
|
||||
def bzr_diff_command(self, file_name):
|
||||
return [self.get_user_command('bzr') or 'bzr', 'diff', file_name]
|
||||
|
||||
def hg_diff_command(self, file_name):
|
||||
return [self.get_user_command('hg') or 'hg', 'diff', file_name]
|
||||
|
||||
|
||||
class ShowDiffCommand(DiffCommand, sublime_plugin.TextCommand):
|
||||
def diff_done(self, result):
|
||||
if not result.strip():
|
||||
return
|
||||
|
||||
file_name = re.findall(r'([^\\\/]+)$', self.view.file_name())
|
||||
self.scratch(result, title="Diff - " + file_name[0])
|
||||
|
||||
|
||||
class DiffParser(object):
|
||||
instance = None
|
||||
|
||||
def __init__(self, diff):
|
||||
self.diff = diff
|
||||
self.chunks = None
|
||||
self.__class__.instance = self
|
||||
|
||||
def _append_to_chunks(self, start, lines):
|
||||
self.chunks.append({
|
||||
"start": start,
|
||||
"end": start + len(lines),
|
||||
"lines": lines
|
||||
})
|
||||
|
||||
def get_chunks(self):
|
||||
if self.chunks is None:
|
||||
self.chunks = []
|
||||
diff = self.diff.strip()
|
||||
if diff:
|
||||
re_header = re.compile(r'^@@[0-9\-, ]+\+(\d+)', re.S)
|
||||
current = None
|
||||
lines = []
|
||||
for line in diff.splitlines():
|
||||
# ignore lines with '\' at the beginning
|
||||
if line.startswith('\\'):
|
||||
continue
|
||||
|
||||
matches = re.findall(re_header, line)
|
||||
if matches:
|
||||
if current is not None:
|
||||
self._append_to_chunks(current, lines)
|
||||
current = int(matches[0])
|
||||
lines = []
|
||||
elif current:
|
||||
lines.append(line)
|
||||
if current is not None and lines:
|
||||
self._append_to_chunks(current, lines)
|
||||
|
||||
return self.chunks
|
||||
|
||||
def get_lines_to_hl(self):
|
||||
inserted = []
|
||||
changed = []
|
||||
deleted = []
|
||||
|
||||
for chunk in self.get_chunks():
|
||||
current = chunk['start']
|
||||
deleted_line = None
|
||||
for line in chunk['lines']:
|
||||
if line.startswith('-'):
|
||||
if (not deleted_line or deleted_line not in deleted):
|
||||
deleted.append(current)
|
||||
deleted_line = current
|
||||
elif line.startswith('+'):
|
||||
if deleted_line:
|
||||
deleted.pop()
|
||||
deleted_line = None
|
||||
changed.append(current)
|
||||
elif current - 1 in changed:
|
||||
changed.append(current)
|
||||
else:
|
||||
inserted.append(current)
|
||||
current += 1
|
||||
else:
|
||||
deleted_line = None
|
||||
current += 1
|
||||
|
||||
return inserted, changed, deleted
|
||||
|
||||
def get_original_part(self, line_num):
|
||||
""" returns a chunk of code that relates to the given line
|
||||
and was there before modifications
|
||||
|
||||
return (lines list, start_line int, replace_lines int)
|
||||
"""
|
||||
|
||||
# for each chunk from diff:
|
||||
for chunk in self.get_chunks():
|
||||
# if line_num is within that chunk
|
||||
if chunk['start'] <= line_num <= chunk['end']:
|
||||
ret_lines = []
|
||||
current = chunk['start'] # line number that corresponds to current version of file
|
||||
first = None # number of the first line to change
|
||||
replace_lines = 0 # number of lines to change
|
||||
return_this_lines = False # flag shows whether we can return accumulated lines
|
||||
for line in chunk['lines']:
|
||||
if line.startswith('-') or line.startswith('+'):
|
||||
first = first or current
|
||||
if current == line_num:
|
||||
return_this_lines = True
|
||||
if line.startswith('-'):
|
||||
# if line starts with '-' we have previous version
|
||||
ret_lines.append(line[1:])
|
||||
else:
|
||||
# if line starts with '+' we only increment numbers
|
||||
replace_lines += 1
|
||||
current += 1
|
||||
elif return_this_lines:
|
||||
break
|
||||
else:
|
||||
# gap between modifications
|
||||
# reset our variables
|
||||
current += 1
|
||||
first = current
|
||||
replace_lines = 0
|
||||
ret_lines = []
|
||||
if return_this_lines:
|
||||
return ret_lines, first, replace_lines
|
||||
|
||||
return None, None, None
|
||||
|
||||
|
||||
class HlChangesCommand(DiffCommand, sublime_plugin.TextCommand):
|
||||
def hl_lines(self, lines, hl_key):
|
||||
if (not len(lines)):
|
||||
self.view.erase_regions(hl_key)
|
||||
return
|
||||
|
||||
icon = self.settings.get('region_icon') or 'modific'
|
||||
if icon == 'modific':
|
||||
if IS_ST3:
|
||||
icon = 'Packages/Modific/icons/' + hl_key + '.png'
|
||||
else:
|
||||
icon = '../Modific/icons/' + hl_key
|
||||
points = [self.view.text_point(l - 1, 0) for l in lines]
|
||||
regions = [sublime.Region(p, p) for p in points]
|
||||
self.view.add_regions(hl_key, regions, "markup.%s.diff" % hl_key,
|
||||
icon, sublime.HIDDEN | sublime.DRAW_EMPTY)
|
||||
|
||||
def diff_done(self, diff):
|
||||
if diff and '@@' not in diff:
|
||||
# probably this is an error message
|
||||
# if print raise UnicodeEncodeError, try to encode string to utf-8 (issue #35)
|
||||
try:
|
||||
print(diff)
|
||||
except UnicodeEncodeError:
|
||||
print(diff.encode('utf-8'))
|
||||
|
||||
diff_parser = DiffParser(diff)
|
||||
(inserted, changed, deleted) = diff_parser.get_lines_to_hl()
|
||||
|
||||
if self.settings.get('debug'):
|
||||
print(inserted, changed, deleted)
|
||||
self.hl_lines(inserted, 'inserted')
|
||||
self.hl_lines(deleted, 'deleted')
|
||||
self.hl_lines(changed, 'changed')
|
||||
|
||||
|
||||
class ShowOriginalPartCommand(DiffCommand, sublime_plugin.TextCommand):
|
||||
def run(self, edit):
|
||||
diff_parser = DiffParser.instance
|
||||
if not diff_parser:
|
||||
return
|
||||
|
||||
(row, col) = self.view.rowcol(self.view.sel()[0].begin())
|
||||
(lines, start, replace_lines) = diff_parser.get_original_part(row + 1)
|
||||
if lines is not None:
|
||||
self.panel(os.linesep.join(lines))
|
||||
|
||||
|
||||
class ReplaceModifiedPartCommand(DiffCommand, sublime_plugin.TextCommand):
|
||||
def run(self, edit):
|
||||
self.view.run_command('save')
|
||||
|
||||
diff_parser = DiffParser.instance
|
||||
if not diff_parser:
|
||||
return
|
||||
|
||||
(row, col) = self.view.rowcol(self.view.sel()[0].begin())
|
||||
(lines, current, replace_lines) = diff_parser.get_original_part(row + 1)
|
||||
if self.settings.get('debug'):
|
||||
print('replace', (lines, current, replace_lines))
|
||||
if lines is not None:
|
||||
begin = self.view.text_point(current - 1, 0)
|
||||
content = os.linesep.join(lines)
|
||||
if replace_lines:
|
||||
end = self.view.line(self.view.text_point(replace_lines + current - 2, 0)).end()
|
||||
region = sublime.Region(begin, end)
|
||||
if lines:
|
||||
self.view.run_command('edit_view', dict(command='replace', region=[region.begin(), region.end()], output=content))
|
||||
else:
|
||||
region = self.view.full_line(region)
|
||||
self.view.run_command('edit_view', dict(command='erase', region=[region.begin(), region.end()]))
|
||||
else:
|
||||
self.view.run_command('edit_view', dict(command='insert', begin=begin, output=content + os.linesep))
|
||||
self.view.run_command('save')
|
||||
|
||||
|
||||
class HlChangesBackground(sublime_plugin.EventListener):
|
||||
def on_load(self, view):
|
||||
view.run_command('hl_changes')
|
||||
|
||||
def on_activated(self, view):
|
||||
view.run_command('hl_changes')
|
||||
|
||||
def on_post_save(self, view):
|
||||
view.run_command('hl_changes')
|
||||
|
||||
|
||||
class JumpBetweenChangesCommand(DiffCommand, sublime_plugin.TextCommand):
|
||||
def run(self, edit, direction='next'):
|
||||
lines = self._get_lines()
|
||||
if not lines:
|
||||
return
|
||||
|
||||
if direction == 'prev':
|
||||
lines.reverse()
|
||||
|
||||
(current_line, col) = self.view.rowcol(self.view.sel()[0].begin())
|
||||
current_line += 1
|
||||
jump_to = None
|
||||
for line in lines:
|
||||
if direction == 'next' and current_line < line:
|
||||
jump_to = line
|
||||
break
|
||||
if direction == 'prev' and current_line > line:
|
||||
jump_to = line
|
||||
break
|
||||
|
||||
if not jump_to:
|
||||
jump_to = lines[0]
|
||||
|
||||
self.goto_line(edit, jump_to)
|
||||
|
||||
def goto_line(self, edit, line):
|
||||
# Convert from 1 based to a 0 based line number
|
||||
line = int(line) - 1
|
||||
|
||||
# Negative line numbers count from the end of the buffer
|
||||
if line < 0:
|
||||
lines, _ = self.view.rowcol(self.view.size())
|
||||
line = lines + line + 1
|
||||
|
||||
pt = self.view.text_point(line, 0)
|
||||
|
||||
self.view.sel().clear()
|
||||
self.view.sel().add(sublime.Region(pt))
|
||||
|
||||
self.view.show(pt)
|
||||
|
||||
def _get_lines(self):
|
||||
diff_parser = DiffParser.instance
|
||||
if not diff_parser:
|
||||
return
|
||||
|
||||
(inserted, changed, deleted) = diff_parser.get_lines_to_hl()
|
||||
lines = list(set(inserted + changed + deleted))
|
||||
lines.sort()
|
||||
|
||||
prev = None
|
||||
ret_lines = []
|
||||
for line in lines:
|
||||
if prev != line - 1:
|
||||
ret_lines.append(line)
|
||||
prev = line
|
||||
|
||||
return ret_lines
|
||||
|
||||
|
||||
class UncommittedFilesCommand(VcsCommand, sublime_plugin.WindowCommand):
|
||||
def active_view(self):
|
||||
return self.window.active_view()
|
||||
|
||||
def run(self):
|
||||
self.root, self.vcs = vcs_root(self.get_working_dir())
|
||||
status_command = getattr(self, '{0}_status_command'.format(self.vcs['name']), None)
|
||||
if status_command:
|
||||
self.run_command(status_command(), self.status_done, working_dir=self.root)
|
||||
|
||||
def git_status_command(self):
|
||||
return [self.get_user_command('git') or 'git', 'status', '--porcelain']
|
||||
|
||||
def svn_status_command(self):
|
||||
return [self.get_user_command('svn') or 'svn', 'status', '--quiet']
|
||||
|
||||
def bzr_status_command(self):
|
||||
return [self.get_user_command('bzr') or 'bzr', 'status', '-S', '--no-pending', '-V']
|
||||
|
||||
def hg_status_command(self):
|
||||
return [self.get_user_command('hg') or 'hg', 'status']
|
||||
|
||||
def git_status_file(self, file_name):
|
||||
# first 2 characters are status codes, the third is a space
|
||||
return file_name[3:]
|
||||
|
||||
def svn_status_file(self, file_name):
|
||||
return file_name[8:]
|
||||
|
||||
def bzr_status_file(self, file_name):
|
||||
return file_name[4:]
|
||||
|
||||
def hg_status_file(self, file_name):
|
||||
return file_name[2:]
|
||||
|
||||
def status_done(self, result):
|
||||
self.results = list(filter(lambda x: len(x) > 0 and not x.lstrip().startswith('>'),
|
||||
result.rstrip().split('\n')))
|
||||
if len(self.results):
|
||||
self.show_status_list()
|
||||
else:
|
||||
sublime.status_message("Nothing to show")
|
||||
|
||||
def show_status_list(self):
|
||||
self.get_window().show_quick_panel(self.results, self.panel_done,
|
||||
sublime.MONOSPACE_FONT)
|
||||
|
||||
def panel_done(self, picked):
|
||||
if 0 > picked < len(self.results):
|
||||
return
|
||||
picked_file = self.results[picked]
|
||||
get_file = getattr(self, '{0}_status_file'.format(self.vcs['name']), None)
|
||||
if (get_file):
|
||||
self.open_file(get_file(picked_file))
|
||||
|
||||
def open_file(self, picked_file):
|
||||
if os.path.isfile(os.path.join(self.root, picked_file)):
|
||||
self.window.open_file(os.path.join(self.root, picked_file))
|
||||
else:
|
||||
sublime.status_message("File doesn't exist")
|
@@ -0,0 +1,6 @@
|
||||
[
|
||||
{
|
||||
"caption": "Modific: Show diff",
|
||||
"command": "show_diff"
|
||||
}
|
||||
]
|
@@ -0,0 +1,33 @@
|
||||
// Modific default settings
|
||||
{
|
||||
// Name of a region icon
|
||||
// Valid icon names are: modific, dot, circle, bookmark and cross
|
||||
// WARNING: if you set value different than 'modific',
|
||||
// you may experience issues with UI of Sublime.
|
||||
// See https://github.com/gornostal/Modific/issues/9
|
||||
"region_icon": "modific",
|
||||
|
||||
// You can use your commands instead of plain "git" or "svn"
|
||||
// e.g. "/usr/bin/git" or "C:\bin\git.exe"
|
||||
"vcs": [
|
||||
["git", "git"],
|
||||
["svn", "svn"],
|
||||
["bzr", "bzr"],
|
||||
["hg" , "hg"]
|
||||
],
|
||||
|
||||
//if you have some weird OS, that has non-unicode console
|
||||
//place its console encoding here
|
||||
"console_encoding" : "",
|
||||
|
||||
// if true, plugin prints some debug information to the console window
|
||||
"debug": false,
|
||||
|
||||
// set to false to disable automatic saving
|
||||
"autosave": true,
|
||||
|
||||
"svn_use_internal_diff": true,
|
||||
|
||||
// File size limit (in KB) for drawing icons on the gutter
|
||||
"max_file_size": 1024
|
||||
}
|
@@ -0,0 +1,86 @@
|
||||
Modific
|
||||
=========
|
||||
|
||||
Modific is a ST2(3) plugin for highlighting lines changed since the last commit (you know what I mean if you used Netbeans).
|
||||
|
||||
For now it supports **Git**, **SVN**, **Bazaar** and **Mercurial**.
|
||||
|
||||
|
||||
Install
|
||||
-------
|
||||
|
||||
The easiest way to install is through **[Package Control](http://wbond.net/sublime\_packages/package\_control)**.
|
||||
|
||||
Once you install Package Control, restart ST3 and bring up the Command Palette (`Ctrl+Shift+P` on Linux/Windows, `Cmd+Shift+P` on OS X). Select "Package Control: Install Package", wait while Package Control fetches the latest package list, then select *Modific* when the list appears. The advantage of using this method is that Package Control will automatically keep *Modific* up to date with the latest version.
|
||||
|
||||
Or you can **download** the latest source from [GitHub](https://github.com/gornostal/Modific/zipball/master) and copy the *Modific* folder to your Sublime Text "Packages" directory.
|
||||
|
||||
Or **clone** the repository to your Sublime Text "Packages" directory:
|
||||
|
||||
git clone git://github.com/gornostal/Modific.git
|
||||
|
||||
|
||||
The "Packages" directory is located at:
|
||||
|
||||
* OS X:
|
||||
|
||||
~/Library/Application Support/Sublime Text 2/Packages/
|
||||
|
||||
* Linux:
|
||||
|
||||
~/.config/sublime-text-2/Packages/
|
||||
|
||||
* Windows:
|
||||
|
||||
%APPDATA%/Sublime Text 2/Packages/
|
||||
|
||||
Please, make sure your VCS binaries is in the PATH (**especially if you are on Windows**).
|
||||
|
||||
To do that on Windows, open `Controll Panel -> System -> Advanced system settings -> Environment variables -> System Variables`, find PATH, click "Edit" and append `;C:\path\to\VCS\binaries` for every VCS you will use (or make sure it's already there).
|
||||
|
||||
Features / Usage
|
||||
----------------
|
||||
|
||||
**Highlight changes** *(automatically: on save or when window gets focus)*
|
||||
[](http://i.imgur.com/FgpyR.jpg)
|
||||
|
||||
**Show diff** `Ctrl+Alt+D` on Linux/Windows and OS X
|
||||
[](http://i.imgur.com/csCw7.jpg)
|
||||
|
||||
**Preview of the commited code for current line** `Ctrl+Alt+C` on Linux/Windows, `Ctlr+Super+C` on OS X
|
||||
[](http://i.imgur.com/siVOX.jpg)
|
||||
|
||||
**Revert modification** `Ctrl+Alt+R` on Linux/Windows, `Ctlr+Super+R` on OS X
|
||||
|
||||
This command reverts modifications if your cursor stays on modified line (or if on group of lines, then whole group will be reverted)
|
||||
|
||||
**View uncommitted files in a quick panel** `Ctrl+Alt+U` on Linux/Windows, `Ctlr+Super+U` on OS X
|
||||
[](http://i.imgur.com/sldHN.jpg)
|
||||
|
||||
**Go through changed lines** `Ctrl+Shift+Page Up(Down)`
|
||||
|
||||
For those who expected to see a clone of Netbeans feature - unfortunately, with existing Sublime Text API that is impossible :(
|
||||
|
||||
[Discussion on the forum](http://www.sublimetext.com/forum/viewtopic.php?f=5&t=7468)
|
||||
|
||||
Configuring
|
||||
-----------
|
||||
|
||||
Open `Prefrences -> Package Settings -> Modific -> Settings - Default` and look for available settings.
|
||||
|
||||
If you want to change something, don't do it in this file. Open `Preferences -> Package Settings -> Modific -> Settings - User` and put there your configuration.
|
||||
|
||||
You can configure is a type of icon (dot, circle or bookmark) and path for your VCS binaries (or leave them as is, if you have them in your PATH). It's also possible to set priority for VCS used (when you have more than one simultaneously) by reordering their definitions.
|
||||
|
||||
If some sacred punishment has been bestowed upon you, and you have no other choice but to use OS, where console has non-UTF8 encoding, you can set console_encoding parameter to the name of your beloved encoding. This parameter is specifically designed for Windows XP users, who have their git repositories in folders with cyrillic path. Since russian XP uses CP1251 as default encoding (including console), VCS diff commands will be encoded appropriately, when using this parameter.
|
||||
|
||||
If you use different than the default theme, you can customize colors of bullets on the gutter by adding [this](https://gist.github.com/3692073) chunk of code to your theme.
|
||||
|
||||
Thanks to
|
||||
---------
|
||||
|
||||
@beefsack for purchasing a license
|
||||
|
||||
License
|
||||
-------
|
||||
Released under the [WTFPLv2](http://sam.zoy.org/wtfpl/COPYING).
|
Binary file not shown.
After Width: | Height: | Size: 96 B |
Binary file not shown.
After Width: | Height: | Size: 95 B |
Binary file not shown.
After Width: | Height: | Size: 95 B |
@@ -0,0 +1 @@
|
||||
{"url": "https://github.com/gornostal/Modific", "version": "2013.03.01.06.02.08", "description": "Highlight lines changed since the last commit (supports Git, SVN, Bazaar and Mercurial) / ST2(3) plugin"}
|
Reference in New Issue
Block a user